This script is very similar to degree_shifts_projected, but looks at 2˚ shifts instead of 1˚shifts.
Degree shifts
Second part of paper, if we move away from equator by 2˚ degree, how much habitat do we gain or lost
I need to look at shelf area by degrees latitude
library(raster)
library(sf)
library(ncdf4)
library(rmapshaper)
library(tidyverse)
library(diptest)
library(moments)
library(viridis) #colors
library(data.table)
library(hydroTSM) #hypsometric curves
library(gridExtra)
library(maptools)
library(rgdal)
library(rgeos)
library(SpaDES)
library(rnaturalearth)
library(rnaturalearthdata)
etopo_shelf_df <- readRDS("~/Documents/grad school/Rutgers/Repositories/shelf_habitat_distribution/etopo_shelf_df.rds")
#bring in bathymetry data frame for shelf regions
#LMEs
LME_spdf <- readOGR("LME66/LMEs66.shp") #spatial points data frame with all 66 LMEs
OGR data source with driver: ESRI Shapefile
Source: "/Users/zoekitchel/Documents/grad school/Rutgers/Repositories/shelf_habitat_distribution/LME66/LMEs66.shp", layer: "LMEs66"
with 66 features
It has 9 fields
Integer64 fields read as strings: OBJECTID
#pull in shapefile for FAO statistical regions: 3/11/2021: http://www.fao.org/geonetwork/srv/en/main.home?uuid=ac02a460-da52-11dc-9d70-0017f293bd28
FAO_spdf <- readOGR("FAO_AREAS/FAO_AREAS.shp")
OGR data source with driver: ESRI Shapefile
Source: "/Users/zoekitchel/Documents/grad school/Rutgers/Repositories/shelf_habitat_distribution/FAO_AREAS/FAO_AREAS.shp", layer: "FAO_AREAS"
with 324 features
It has 15 fields
#convert to equal area projection
#equalareaprojection<- crs(" +proj=eqearth ")
#The Lambert azimuthal equal-area projection is a particular mapping from a sphere to a disk. It accurately represents area in all regions of the sphere, but it does not accurately represent angles.
equalareaprojection<- crs(" +proj=laea ")
Make bathymetry data frame into raster (this takes a bit)
Note that I am trying to use the equal area projection
etopo_shelf_raster <- rasterFromXYZ(etopo_shelf_df, crs = crs(LME_spdf))
#reclassify all values <2000m in depth to 1 instead of actual depth
etopo_shelf_raster <- reclassify(etopo_shelf_raster,cbind(-Inf, Inf, 1))
Should go by projections of where species are moving: “Marine species (~80% being ectotherms in the database; Extended Data Fig. 2) have moved towards the poles at a mean (±s.e.m.) pace of 5.92 ± 0.94 km yr−1 (one-sample Student’s t-test: t=6.26; d.f. residuals=23; P=2.20×10–6), which is almost six times faster than terrestrial species (one-way analysis of variance (ANOVA): F=12.68; d.f. factor=1; d.f. residuals=45; P=8.88×10–4).” Lenoir 2020
5.92 km * 10 = 59.2 km in 10 years
59.2 km is how many degrees?
1° = 111 km, 2˚ = 222/59.2= 3.75, so roughly 35 years so,
222/59.2
[1] 3.75
0.5333˚ is representative of decadal shifts, but, for better visualization let’s go with 2˚ (representative of 40 year shifts)
I will put areas into 2˚ Bins (180 total degrees, so 180/2=90 total latitudinal bins)
180/2
[1] 90
How does continental shelf habitat change with latitude?
Look at contiguous coast lines.
I am going to leave out Antarctica (61) and the Arctic (64) as Antarctica drowned out patterns in lower latitudes and Arctic doesn’t have much habitat shallower than 2000m to begin with.
NB: For subarctic regions, I’m going by this figure where there appear to be splits between Europe and Greenland, Greenland and Canada, and Canada and Alaska. This is for sure up for discussion, but it seems like once species get above the continents, it is a big of a free for all.
Eastern Atlantic (3) -19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 58, 59, 60, 62
3/8 update (make more LMEs serve multiple coastlines, get rid of detached islands (New Zealand, Greenland, Iceland), and inner islands (aka Baltic Sea, mediterrraniean sea, red sea, black sea))
-20, 58, 57, 56, 21, 60, 22, 23* Baltic Sea, 24, 25, 27, 28, 29, 26 * Mediterranean, 62* Black Sea
Western Atlantic (2) - 5, 6, 7, 8, 9, 12, 14, 15, 16, 17, 18, 63, 66
-3/8 update, LEAVE OUT GREENLAND
- 66, 55, 63** Hudson Bay, 9, 8, 7, 6, 5** GOM, 12, 17, 16, 15, 14 **what about gulf of mexico and gulf of California?
Eastern Pacific (1) - 1, 2, 3, 4, 11, 13, 54, 55 **Beaufort sea thinly linked to Chukchi Sea, so split here, 65
-3/8 update -54, 1, 2, 53, 65, 3, 4** GOC, 11, 13
Western Indian (4) -30, 31, 32, 33
-3/8 update -30, 31, 33**black sea, 32
Eastern Indian (5) -34, 38, 43, 44, 45
-3/8 update -just use FAO statistical area 57
Western Pacific (6) 1, 35, 36, 37, 39, 40, 41, 42, 46, 47, 48, 49, 50, 51, 52, 53, 54, 56, 57, 65
-3/8 update -42, 41, 50, 51, 52, 53, 1,54, 56, 57, 58, 20,and FAO statistical regions 71 and 61
Merge LMEs into 6 coastline regions
Masks for regions that are not included in LMEs (coordinates taken from Google maps). Will add in areas that do NOT overlap with existing LMEs at the end.
#Western Pacific, include FAO statistical regions 71 and 61
west_pac_fao <- FAO_spdf[FAO_spdf$F_CODE %in% c(61,71),]
#adjust extent a bit to get rid of rogue -178 points
west_pac_fao <- crop(west_pac_fao, extent(94, 180, -28.15, 66.4148))
west_pac_fao.raster <- crop(etopo_shelf_raster, extent(94, 180, -28.15, 66.4148))
west_pac_fao.raster <- mask(west_pac_fao.raster, west_pac_fao)
#Eastern Indian Ocean
east_ind_fao <- FAO_spdf[FAO_spdf$F_CODE == 57,]
west_pac <- c(42, 41, 50, 51, 52, 53, 1,54, 56, 57, 58, 20)
east_pac <- c(54, 1, 2, 53, 65, 3, 4, 11, 13)
west_atl <- c(66, 55, 63, 9, 8, 7, 6, 5, 12, 17, 16, 15, 14)
east_atl <- c(20, 58, 57, 56, 21, 60, 22, 23, 24, 25, 27, 28, 29, 26, 62)
west_ind <- c(30, 31, 33, 32)
#subregions based on LME_number
west_pac_spdf <- LME_spdf[(LME_spdf$LME_NUMBER) %in% west_pac,]
east_pac_spdf <- LME_spdf[(LME_spdf$LME_NUMBER) %in% east_pac,]
west_atl_spdf <- LME_spdf[(LME_spdf$LME_NUMBER) %in% west_atl,]
west_ind_spdf <- LME_spdf[(LME_spdf$LME_NUMBER) %in% west_ind,]
east_atl_spdf <- LME_spdf[(LME_spdf$LME_NUMBER) %in% east_atl,]
east_ind_spdf <- east_ind_fao
#for subregions that span 360, we need to change CRS a bit
newCRS_west <- "+proj=longlat +datum=WGS84 +lon_wrap=180" #this shifts 180 degrees
west_pac_spdf_shift <- spTransform(west_pac_spdf, CRS(newCRS_west))
west_pac_spdf_shift <- gBuffer(west_pac_spdf_shift, byid=TRUE, width=0) #gets rid of buffers, allows for union
Spatial object is not projected; GEOS expects planar coordinates
newCRS_east <- "+proj=longlat +datum=WGS84 +lon_wrap=180" #this shifts 180 degrees
east_pac_spdf_shift <- spTransform(east_pac_spdf, CRS(newCRS_east))
east_pac_spdf_shift <- gBuffer(east_pac_spdf_shift, byid=TRUE, width=0) #gets rid of buffers, allows for union
Spatial object is not projected; GEOS expects planar coordinates
#rotate raster for bathymetry (guided by extent of etoposhelf raster, that's why wacky #s)
x1 <- crop(etopo_shelf_raster, extent(-180.0167, -0.0167, -90.01667, 90.01667))
x2 <- crop(etopo_shelf_raster, extent(0, 180.0167, -90.01667, 90.01667))
extent(x1) <- c(180.0167, 360.0167, -90.01667 , 90.01667)
etopo_shelf_raster_180 <- merge(x1, x2)
#get rid of buffer for east atl as well to allow for union
east_atl_spdf_nobuf <- gBuffer(east_atl_spdf, byid=TRUE, width=0)
Spatial object is not projected; GEOS expects planar coordinates
region_names <- c("west_pac_spdf_shift", "east_pac_spdf_shift", "west_atl_spdf", "west_ind_spdf", "east_atl_spdf_nobuf", "east_ind_spdf")
#dissolve all polygons by region
for (i in 1:length(region_names)) {
name <- paste0(region_names[i], "_agg")
assign(name, gUnaryUnion(get(region_names[i]))) #dissolve polygons within coastline region into one
}
Extract bathymetry data from polygon only to make sure we’re limiting to shelf regions above 2000 meters
region_names_shift <- region_names[1:2]
region_names_noshift <- region_names[3:6]
for (i in 1:length(region_names_noshift)) {
#crop bathymetry layer to LME subset (continental shelf habitat in LMEs)
raster_extent <-
crop(etopo_shelf_raster, extent(get(paste0(region_names_noshift[i], "_agg"))))
#which areas of raster fall within borders?
assign(paste0(region_names_noshift[i], "_mask"),
mask(raster_extent, get(paste0(region_names_noshift[i], "_agg"))))
assign(paste0(region_names_noshift[i], "_mask_1s"),
reclassify(get(paste0(region_names_noshift[i], "_mask")), cbind(-Inf, Inf, 1)))
}
#edit for east_pac_spdf_shift and west_pac_spdf_shift (+180˚)
for (i in 1:length(region_names_shift[1:2])) {
#crop bathy layer to LME subset
raster_extent <-
crop(etopo_shelf_raster_180, extent(get(paste0(region_names_shift[i], "_agg"))))
#which areas of raster fall within borders?
assign(paste0(region_names_shift[i], "_mask"),
mask(raster_extent, get(paste0(region_names_shift[i], "_agg"))))
assign(paste0(region_names_shift[i], "_mask_1s"),
reclassify(get(paste0(region_names_shift[i], "_mask")), cbind(-Inf, Inf, 1)))
}
Merge in areas unaccounted for by LMEs (Philippines, Sumatra, Papua New Guinea)
#western pacific ocean
west_pac_spdf_mask_full <- merge(west_pac_fao.raster, west_pac_spdf_shift_mask_1s)
How to calculate area?
raster::area() THIS IS WHAT WE’RE GOING WITH Raster objects: Compute the approximate surface area of cells in an unprojected (longitude/latitude) Raster object. It is an approximation because area is computed as the height (latitudinal span) of a cell (which is constant among all cells) times the width (longitudinal span) in the (latitudinal) middle of a cell. The width is smaller at the poleward side than at the equator-ward side of a cell. This variation is greatest near the poles and the values are thus not very precise for very high latitudes. If x is a Raster* object: RasterLayer or RasterBrick. Cell values represent the size of the cell in km2, or the relative size if weights=TRUE
raster::area() SpatialPolygons: Compute the area of the spatial features. Works for both planar and angular (lon/lat) coordinate reference systems. If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
rgeos::gArea Returns the area of the geometry in the units of the current projection. By definition non-[MULTI]POLYGON geometries have an area of 0. The area of a POLYGON is the area of its shell less the area of any holes. Note that this value may be different from the area slot of the Polygons class as this value does not subtract the area of any holes in the geometry.
Now, we will split each coastline raster into latitudinal bins of 2˚
If these have already been done, load in from file instead of recreating because that takes some time.
load("west_pac_shelf_areas_2degrees.Rdata")
load("west_atl_shelf_areas_2degrees.Rdata")
load("west_ind_shelf_areas_2degrees.Rdata")
load("east_pac_shelf_areas_2degrees.Rdata")
load("east_atl_shelf_areas_2degrees.Rdata")
load("east_ind_shelf_areas_2degrees.Rdata")
Western Pacific
north_extent <- c(xmin(west_pac_spdf_mask_full), xmax(west_pac_spdf_mask_full), 0, ymax(west_pac_spdf_mask_full))
south_extent <- c(xmin(west_pac_spdf_mask_full), xmax(west_pac_spdf_mask_full), ymin(west_pac_spdf_mask_full), 0)
#crop west_pac raster above and below 0
west_pac_spdf_shift_agg_north <- crop(west_pac_spdf_mask_full, extent(north_extent))
west_pac_spdf_shift_agg_south <- crop(west_pac_spdf_mask_full, extent(south_extent))
#unfortunately, I think I may have to just do this manually (ugly, I know)
#all chunks for west pacific
west_pac_north_latitudes <- seq(0, ymax(west_pac_spdf_mask_full), by = 2)
west_pac_south_latitudes <- seq(0, ymin(west_pac_spdf_mask_full), by = -2)
#setup data table to populate in loop, subtracting one to allow for bins
west_pac_shelf_areas <- as.data.table(matrix(nrow = (length(west_pac_north_latitudes)-1+length(west_pac_south_latitudes)-1)))
west_pac_shelf_areas[, latitude_start := as.numeric(V1)][, latitude_end := as.numeric(V1)][, area_rasterarea := as.numeric(V1)][, area_equalareaproj := as.numeric(V1)][, area_rgeos_gArea := as.numeric(V1)][, V1 := NULL]
#loop for north
for (i in 1:(length(west_pac_north_latitudes)-1)) {
#setting up extent for slicing by min and max longitudes, and i to i+1 latitudes
north_extent <- c(xmin(west_pac_spdf_mask_full), xmax(west_pac_spdf_mask_full), west_pac_north_latitudes[i], west_pac_north_latitudes[i+1])
#crop raster segement based on bin extent
segment_north <- crop(west_pac_spdf_mask_full, extent(north_extent))
#populate data table with latitudinal bin
west_pac_shelf_areas[i, "latitude_start"] <- west_pac_north_latitudes[i]
west_pac_shelf_areas[i, "latitude_end"] <- west_pac_north_latitudes[i+1]
if(all(is.na(values(segment_north)))) { #if there's no shelf area within a bin, all area = 0
west_pac_shelf_areas[i, "area_equalareaproj"] <- 0
west_pac_shelf_areas[i, "area_rasterarea"] <- 0
west_pac_shelf_areas[i, "area_rgeos_gArea"] <- 0
print(i)
} else { #if there is shelf area within the bin, calculate area of slice
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_north, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_north)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster) #in km^2
#populate data table with raster area
west_pac_shelf_areas[i, "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_north.sp <- rasterToPolygons(segment_north, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_north.sp.EA <- spTransform(segment_north.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_north.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with polygon area using raster calculation
west_pac_shelf_areas[i, "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_north.sp.EA)/1e6
#populate data table with polygon area using regeos calculation
west_pac_shelf_areas[i, "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24
[1] 25
[1] 26
[1] 27
[1] 28
[1] 29
[1] 30
[1] 31
[1] 32
[1] 33
[1] 34
[1] 35
[1] 36
[1] 37
[1] 38
[1] 39
[1] 40
[1] 41
#loop for south
for (i in 1:(length(west_pac_south_latitudes)-1)) {
south_extent <- c(xmin(west_pac_spdf_mask_full), xmax(west_pac_spdf_mask_full), west_pac_south_latitudes[i+1], west_pac_south_latitudes[i]) #order= xmin, xmax, ymin, ymax)
#raster segment
segment_south <- crop(west_pac_spdf_mask_full, extent(south_extent))
#add latitude bin info to data table
west_pac_shelf_areas[i+(length(west_pac_north_latitudes)-1), "latitude_start"] <- west_pac_south_latitudes[i]
west_pac_shelf_areas[i+(length(west_pac_north_latitudes)-1), "latitude_end"] <- west_pac_south_latitudes[i+1]
if(all(is.na(values(segment_south)))) { #if there's no shelf area within a bin, meaning there's no shelf area at that latitude
west_pac_shelf_areas[i+(length(west_pac_north_latitudes)-1), "area_equalareaproj"] <- 0
west_pac_shelf_areas[i+(length(west_pac_north_latitudes)-1), "area_rasterarea"] <- 0
west_pac_shelf_areas[i+(length(west_pac_north_latitudes)-1), "area_rgeos_gArea"] <- 0
print(i)
} else {
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_south, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_south)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster)
#populate data table with raster area
west_pac_shelf_areas[i+(length(west_pac_north_latitudes)-1), "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_south.sp <- rasterToPolygons(segment_south, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_south.sp.EA <- spTransform(segment_south.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_south.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with area of polygon from raster::area function
west_pac_shelf_areas[i+(length(west_pac_north_latitudes)-1), "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_south.sp.EA)/1e6
#populate data table from rgeos area calculation for projected polygon
west_pac_shelf_areas[i+(length(west_pac_north_latitudes)-1), "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
#compare raster:area calculation, to equal area still using raster::area function, to rgeos::gArea function for polygons
ggplot(data = west_pac_shelf_areas) +
geom_point(aes(x = latitude_start, y = area_rgeos_gArea), color = "purple", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_rasterarea), color = "darkgreen", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_equalareaproj), color = "red", size = 0.5) +
labs(x=paste0("Latitude ","\u00B0","E"), y = "Area km^2") +
theme_classic()

cor(west_pac_shelf_areas[,3:5], use = "complete.obs")
area_rasterarea area_equalareaproj area_rgeos_gArea
area_rasterarea 1.0000000 0.9999895 0.9999895
area_equalareaproj 0.9999895 1.0000000 1.0000000
area_rgeos_gArea 0.9999895 1.0000000 1.0000000
save(west_pac_shelf_areas, file = "west_pac_shelf_areas_2degrees.Rdata")
load("west_pac_shelf_areas_2degrees.Rdata")
Classify by percent change!!
between 1 and Inf % change = at least 2 fold increase (note I classified any change from 0 to something as NOT a significant change, should return to this conceptually)
between -0.5 and -inf % change = at least 2 fold decrease
between -0.499 and 0.999 = no significant change
For area metric here, I used the raster::area function applied to the projected shapefile
west_pac_shelf_areas[, percent_change := (area_rasterarea-data.table::shift(area_rasterarea, type = "lag"))/data.table::shift(area_rasterarea, type = "lag")][,area_1000s := area_rasterarea/1000]
west_pac_shelf_areas[,hemisphere := ifelse(latitude_end>0,"north","south")]
west_pac_shelf_areas[, change_above_2fold := ifelse((percent_change>=1 & percent_change<Inf), 1, ifelse(percent_change<=-0.5, -1, 0))]
west_pac_shelf_areas_highlight <- west_pac_shelf_areas[change_above_2fold != 0,]
west_pac_shelf_areas_stats <- table(west_pac_shelf_areas[,.(change_above_2fold,hemisphere)])
Model change for southern and northern hemisphere
#add mid latitude
west_pac_shelf_areas[,latitude_mid := abs((latitude_end+latitude_start)/2)]
west_pac_north_mod <- lm(data = west_pac_shelf_areas[hemisphere == "north"], area_rasterarea ~ latitude_mid)
summary(west_pac_north_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = west_pac_shelf_areas[hemisphere ==
"north"])
Residuals:
Min 1Q Median 3Q Max
-271843 -127822 -48263 92044 575518
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 50714 62445 0.812 0.42165
latitude_mid 4378 1319 3.319 0.00196 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 199900 on 39 degrees of freedom
Multiple R-squared: 0.2203, Adjusted R-squared: 0.2003
F-statistic: 11.02 on 1 and 39 DF, p-value: 0.001965
west_pac_south_mod <- lm(data = west_pac_shelf_areas[hemisphere == "south"], area_rasterarea ~ latitude_mid)
summary(west_pac_south_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = west_pac_shelf_areas[hemisphere ==
"south"])
Residuals:
Min 1Q Median 3Q Max
-76592 -57214 -20806 45458 147715
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 272341 29745 9.156 8.87e-09 ***
latitude_mid -6999 1120 -6.247 3.39e-06 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 71280 on 21 degrees of freedom
Multiple R-squared: 0.6502, Adjusted R-squared: 0.6335
F-statistic: 39.03 on 1 and 21 DF, p-value: 3.388e-06
Eastern Pacific
east_pac_spdf_shift
north_extent <- c(xmin(east_pac_spdf_shift_mask_1s), xmax(east_pac_spdf_shift_mask_1s), 0, ymax(east_pac_spdf_shift_mask_1s))
south_extent <- c(xmin(east_pac_spdf_shift_mask_1s), xmax(east_pac_spdf_shift_mask_1s), ymin(east_pac_spdf_shift_mask_1s), 0)
#crop east_pac raster above and below 0
east_pac_spdf_shift_agg_north <- crop(east_pac_spdf_shift_mask_1s, extent(north_extent))
east_pac_spdf_shift_agg_south <- crop(east_pac_spdf_shift_mask_1s, extent(south_extent))
#unfortunately, I think I may have to just do this manually (ugly, I know)
#all chunks for east pacific
east_pac_north_latitudes <- seq(0, ymax(east_pac_spdf_shift_mask_1s), by = 2)
east_pac_south_latitudes <- seq(0, ymin(east_pac_spdf_shift_mask_1s), by = -2)
#setup data table to populate in loop, subtracting one to allow for bins
east_pac_shelf_areas <- as.data.table(matrix(nrow = (length(east_pac_north_latitudes)-1+length(east_pac_south_latitudes)-1)))
east_pac_shelf_areas[, latitude_start := as.numeric(V1)][, latitude_end := as.numeric(V1)][, area_rasterarea := as.numeric(V1)][, area_equalareaproj := as.numeric(V1)][, area_rgeos_gArea := as.numeric(V1)][, V1 := NULL]
#loop for north
for (i in 1:(length(east_pac_north_latitudes)-1)) {
#setting up extent for slicing by min and max longitudes, and i to i+1 latitudes
north_extent <- c(xmin(east_pac_spdf_shift_mask_1s), xmax(east_pac_spdf_shift_mask_1s), east_pac_north_latitudes[i], east_pac_north_latitudes[i+1])
#crop raster segement based on bin extent
segment_north <- crop(east_pac_spdf_shift_mask_1s, extent(north_extent))
#populate data table with latitudinal bin
east_pac_shelf_areas[i, "latitude_start"] <- east_pac_north_latitudes[i]
east_pac_shelf_areas[i, "latitude_end"] <- east_pac_north_latitudes[i+1]
if(all(is.na(values(segment_north)))) { #if there's no shelf area within a bin, all area = 0
east_pac_shelf_areas[i, "area_equalareaproj"] <- 0
east_pac_shelf_areas[i, "area_rasterarea"] <- 0
east_pac_shelf_areas[i, "area_rgeos_gArea"] <- 0
print(i)
} else { #if there is shelf area within the bin, calculate area of slice
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_north, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_north)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster) #in km^2
#populate data table with raster area
east_pac_shelf_areas[i, "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_north.sp <- rasterToPolygons(segment_north, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_north.sp.EA <- spTransform(segment_north.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_north.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with polygon area using raster calculation
east_pac_shelf_areas[i, "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_north.sp.EA)/1e6
#populate data table with polygon area using regeos calculation
east_pac_shelf_areas[i, "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24
[1] 25
[1] 26
[1] 27
[1] 28
[1] 29
[1] 30
[1] 31
[1] 32
[1] 33
[1] 34
[1] 35
[1] 36
[1] 37
[1] 38
[1] 39
#loop for south
for (i in 1:(length(east_pac_south_latitudes)-1)) {
south_extent <- c(xmin(east_pac_spdf_shift_mask_1s), xmax(east_pac_spdf_shift_mask_1s), east_pac_south_latitudes[i+1], east_pac_south_latitudes[i]) #order= xmin, xmax, ymin, ymax)
#raster segment
segment_south <- crop(east_pac_spdf_shift_mask_1s, extent(south_extent))
#add latitude bin info to data table
east_pac_shelf_areas[i+(length(east_pac_north_latitudes)-1), "latitude_start"] <- east_pac_south_latitudes[i]
east_pac_shelf_areas[i+(length(east_pac_north_latitudes)-1), "latitude_end"] <- east_pac_south_latitudes[i+1]
if(all(is.na(values(segment_south)))) { #if there's no shelf area within a bin, meaning there's no shelf area at that latitude
east_pac_shelf_areas[i+(length(east_pac_north_latitudes)-1), "area_equalareaproj"] <- 0
east_pac_shelf_areas[i+(length(east_pac_north_latitudes)-1), "area_rasterarea"] <- 0
east_pac_shelf_areas[i+(length(east_pac_north_latitudes)-1), "area_rgeos_gArea"] <- 0
print(i)
} else {
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_south, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_south)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster)
#populate data table with raster area
east_pac_shelf_areas[i+(length(east_pac_north_latitudes)-1), "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_south.sp <- rasterToPolygons(segment_south, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_south.sp.EA <- spTransform(segment_south.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_south.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with area of polygon from raster::area function
east_pac_shelf_areas[i+(length(east_pac_north_latitudes)-1), "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_south.sp.EA)/1e6
#populate data table from rgeos area calculation for projected polygon
east_pac_shelf_areas[i+(length(east_pac_north_latitudes)-1), "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24
[1] 25
[1] 26
[1] 27
[1] 28
#compare raster:area calculation, to equal area still using raster::area function, to rgeos::gArea function for polygons
ggplot(data = east_pac_shelf_areas) +
geom_point(aes(x = latitude_start, y = area_rgeos_gArea), color = "purple", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_rasterarea), color = "darkgreen", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_equalareaproj), color = "red", size = 0.5) +
labs(x=paste0("Latitude ","\u00B0","E"), y = "Area km^2") +
theme_classic()

cor(east_pac_shelf_areas[,3:5], use = "complete.obs")
area_rasterarea area_equalareaproj area_rgeos_gArea
area_rasterarea 1.0000000 0.9999981 0.9999981
area_equalareaproj 0.9999981 1.0000000 1.0000000
area_rgeos_gArea 0.9999981 1.0000000 1.0000000
save(east_pac_shelf_areas, file = "east_pac_shelf_areas_2degrees.Rdata")
east_pac_shelf_areas[, percent_change := (area_rasterarea-data.table::shift(area_rasterarea, type = "lag"))/data.table::shift(area_rasterarea, type = "lag")][,area_1000s := area_rasterarea/1000]
east_pac_shelf_areas[,hemisphere := ifelse(latitude_end>0,"north","south")]
east_pac_shelf_areas[, change_above_2fold := ifelse((percent_change>=1 & percent_change<Inf), 1, ifelse(percent_change<=-0.5, -1, 0))]
east_pac_shelf_areas_highlight <- east_pac_shelf_areas[change_above_2fold != 0,]
east_pac_shelf_areas_stats <- table(east_pac_shelf_areas[,.(change_above_2fold, hemisphere)])
Model change for southern and northern hemisphere
#add mid latitude
east_pac_shelf_areas[,latitude_mid := abs((latitude_end+latitude_start)/2)]
east_pac_north_mod <- lm(data = east_pac_shelf_areas[hemisphere == "north"], area_rasterarea ~ latitude_mid)
summary(east_pac_north_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = east_pac_shelf_areas[hemisphere ==
"north"])
Residuals:
Min 1Q Median 3Q Max
-149534 -48705 -12794 29647 236105
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -26354.6 22136.0 -1.191 0.241
latitude_mid 2330.7 491.6 4.741 3.13e-05 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 69100 on 37 degrees of freedom
Multiple R-squared: 0.3779, Adjusted R-squared: 0.3611
F-statistic: 22.48 on 1 and 37 DF, p-value: 3.129e-05
east_pac_south_mod <- lm(data = east_pac_shelf_areas[hemisphere == "south"], area_rasterarea ~ latitude_mid)
summary(east_pac_south_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = east_pac_shelf_areas[hemisphere ==
"south"])
Residuals:
Min 1Q Median 3Q Max
-9387 -6276 -1971 4510 19683
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5787.0 3139.0 1.844 0.0767 .
latitude_mid 133.6 97.1 1.376 0.1805
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8301 on 26 degrees of freedom
Multiple R-squared: 0.06789, Adjusted R-squared: 0.03204
F-statistic: 1.894 on 1 and 26 DF, p-value: 0.1805
Western Atlantic
west_atl_spdf_mask
north_extent <- c(xmin(west_atl_spdf_mask_1s), xmax(west_atl_spdf_mask_1s), 0, ymax(west_atl_spdf_mask_1s))
south_extent <- c(xmin(west_atl_spdf_mask_1s), xmax(west_atl_spdf_mask_1s), ymin(west_atl_spdf_mask_1s), 0)
#crop west_atl raster above and below 0
west_atl_spdf_shift_agg_north <- crop(west_atl_spdf_mask_1s, extent(north_extent))
west_atl_spdf_shift_agg_south <- crop(west_atl_spdf_mask_1s, extent(south_extent))
#unfortunately, I think I may have to just do this manually (ugly, I know)
#all chunks for west atlantic
west_atl_north_latitudes <- seq(0, ymax(west_atl_spdf_mask_1s), by = 2)
west_atl_south_latitudes <- seq(0, ymin(west_atl_spdf_mask_1s), by = -2)
#setup data table to populate in loop, subtracting one to allow for bins
west_atl_shelf_areas <- as.data.table(matrix(nrow = (length(west_atl_north_latitudes)-1+length(west_atl_south_latitudes)-1)))
west_atl_shelf_areas[, latitude_start := as.numeric(V1)][, latitude_end := as.numeric(V1)][, area_rasterarea := as.numeric(V1)][, area_equalareaproj := as.numeric(V1)][, area_rgeos_gArea := as.numeric(V1)][, V1 := NULL]
#loop for north
for (i in 1:(length(west_atl_north_latitudes)-1)) {
#setting up extent for slicing by min and max longitudes, and i to i+1 latitudes
north_extent <- c(xmin(west_atl_spdf_mask_1s), xmax(west_atl_spdf_mask_1s), west_atl_north_latitudes[i], west_atl_north_latitudes[i+1])
#crop raster segement based on bin extent
segment_north <- crop(west_atl_spdf_mask_1s, extent(north_extent))
#populate data table with latitudinal bin
west_atl_shelf_areas[i, "latitude_start"] <- west_atl_north_latitudes[i]
west_atl_shelf_areas[i, "latitude_end"] <- west_atl_north_latitudes[i+1]
if(all(is.na(values(segment_north)))) { #if there's no shelf area within a bin, all area = 0
west_atl_shelf_areas[i, "area_equalareaproj"] <- 0
west_atl_shelf_areas[i, "area_rasterarea"] <- 0
west_atl_shelf_areas[i, "area_rgeos_gArea"] <- 0
print(i)
} else { #if there is shelf area within the bin, calculate area of slice
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_north, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_north)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster) #in km^2
#populate data table with raster area
west_atl_shelf_areas[i, "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_north.sp <- rasterToPolygons(segment_north, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_north.sp.EA <- spTransform(segment_north.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_north.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with polygon area using raster calculation
west_atl_shelf_areas[i, "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_north.sp.EA)/1e6
#populate data table with polygon area using regeos calculation
west_atl_shelf_areas[i, "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24
[1] 25
[1] 26
[1] 27
[1] 28
[1] 29
[1] 30
[1] 31
[1] 32
[1] 33
[1] 34
[1] 35
[1] 36
[1] 37
[1] 38
[1] 39
[1] 40
[1] 41
[1] 42
#loop for south
for (i in 1:(length(west_atl_south_latitudes)-1)) {
south_extent <- c(xmin(west_atl_spdf_mask_1s), xmax(west_atl_spdf_mask_1s), west_atl_south_latitudes[i+1], west_atl_south_latitudes[i]) #order= xmin, xmax, ymin, ymax)
#raster segment
segment_south <- crop(west_atl_spdf_mask_1s, extent(south_extent))
#add latitude bin info to data table
west_atl_shelf_areas[i+(length(west_atl_north_latitudes)-1), "latitude_start"] <- west_atl_south_latitudes[i]
west_atl_shelf_areas[i+(length(west_atl_north_latitudes)-1), "latitude_end"] <- west_atl_south_latitudes[i+1]
if(all(is.na(values(segment_south)))) { #if there's no shelf area within a bin, meaning there's no shelf area at that latitude
west_atl_shelf_areas[i+(length(west_atl_north_latitudes)-1), "area_equalareaproj"] <- 0
west_atl_shelf_areas[i+(length(west_atl_north_latitudes)-1), "area_rasterarea"] <- 0
west_atl_shelf_areas[i+(length(west_atl_north_latitudes)-1), "area_rgeos_gArea"] <- 0
print(i)
} else {
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_south, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_south)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster)
#populate data table with raster area
west_atl_shelf_areas[i+(length(west_atl_north_latitudes)-1), "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_south.sp <- rasterToPolygons(segment_south, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_south.sp.EA <- spTransform(segment_south.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_south.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with area of polygon from raster::area function
west_atl_shelf_areas[i+(length(west_atl_north_latitudes)-1), "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_south.sp.EA)/1e6
#populate data table from rgeos area calculation for projected polygon
west_atl_shelf_areas[i+(length(west_atl_north_latitudes)-1), "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24
[1] 25
[1] 26
[1] 27
#compare raster:area calculation, to equal area still using raster::area function, to rgeos::gArea function for polygons
ggplot(data = west_atl_shelf_areas) +
geom_point(aes(x = latitude_start, y = area_rgeos_gArea), color = "purple", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_rasterarea), color = "darkgreen", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_equalareaproj), color = "red", size = 0.5) +
labs(x=paste0("Latitude ","\u00B0","E"), y = "Area km^2") +
theme_classic()

cor(west_atl_shelf_areas[,3:5], use = "complete.obs")
area_rasterarea area_equalareaproj area_rgeos_gArea
area_rasterarea 1.0000000 0.9999826 0.9999826
area_equalareaproj 0.9999826 1.0000000 1.0000000
area_rgeos_gArea 0.9999826 1.0000000 1.0000000
west_atl_shelf_areas[, percent_change := (area_rasterarea-data.table::shift(area_rasterarea, type = "lag"))/data.table::shift(area_rasterarea, type = "lag")][,area_1000s := area_rasterarea/1000]
west_atl_shelf_areas[,hemisphere := ifelse(latitude_end>0,"north","south")]
west_atl_shelf_areas[, change_above_2fold := ifelse((percent_change>=1 & percent_change<Inf), 1, ifelse(percent_change<=-0.5, -1, 0))]
west_atl_shelf_areas_highlight <- west_atl_shelf_areas[change_above_2fold != 0,]
west_atl_shelf_areas_stats <- table(west_atl_shelf_areas[,.(change_above_2fold, hemisphere)])
Model change for southern and northern hemisphere
#add mid latitude
west_atl_shelf_areas[,latitude_mid := abs((latitude_end+latitude_start)/2)]
west_atl_north_mod <- lm(data = west_atl_shelf_areas[hemisphere == "north"], area_rasterarea ~ latitude_mid)
summary(west_atl_north_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = west_atl_shelf_areas[hemisphere ==
"north"])
Residuals:
Min 1Q Median 3Q Max
-86017 -41622 -11628 38179 142471
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 72483.7 18917.6 3.832 0.00044 ***
latitude_mid 1134.8 390.1 2.909 0.00589 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 61290 on 40 degrees of freedom
Multiple R-squared: 0.1746, Adjusted R-squared: 0.154
F-statistic: 8.463 on 1 and 40 DF, p-value: 0.005892
west_atl_south_mod <- lm(data = west_atl_shelf_areas[hemisphere == "south"], area_rasterarea ~ latitude_mid)
summary(west_atl_south_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = west_atl_shelf_areas[hemisphere ==
"south"])
Residuals:
Min 1Q Median 3Q Max
-27966 -21419 -1867 8729 71107
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2423.7 10229.5 0.237 0.815
latitude_mid 1973.7 328.2 6.014 2.78e-06 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 26560 on 25 degrees of freedom
Multiple R-squared: 0.5913, Adjusted R-squared: 0.575
F-statistic: 36.17 on 1 and 25 DF, p-value: 2.783e-06
Eastern Atlantic
east_atl_spdf_nobuf_mask
north_extent <- c(xmin(east_atl_spdf_nobuf_mask_1s), xmax(east_atl_spdf_nobuf_mask_1s), 0, ymax(east_atl_spdf_nobuf_mask_1s))
south_extent <- c(xmin(east_atl_spdf_nobuf_mask_1s), xmax(east_atl_spdf_nobuf_mask_1s), ymin(east_atl_spdf_nobuf_mask_1s), 0)
#crop east_atl raster above and below 0
east_atl_spdf_shift_agg_north <- crop(east_atl_spdf_nobuf_mask_1s, extent(north_extent))
east_atl_spdf_shift_agg_south <- crop(east_atl_spdf_nobuf_mask_1s, extent(south_extent))
#unfortunately, I think I may have to just do this manually (ugly, I know)
#all chunks for east atlantic
east_atl_north_latitudes <- seq(0, ymax(east_atl_spdf_nobuf_mask_1s), by = 2)
east_atl_south_latitudes <- seq(0, ymin(east_atl_spdf_nobuf_mask_1s), by = -2)
#setup data table to populate in loop, subtracting one to allow for bins
east_atl_shelf_areas <- as.data.table(matrix(nrow = (length(east_atl_north_latitudes)-1+length(east_atl_south_latitudes)-1)))
east_atl_shelf_areas[, latitude_start := as.numeric(V1)][, latitude_end := as.numeric(V1)][, area_rasterarea := as.numeric(V1)][, area_equalareaproj := as.numeric(V1)][, area_rgeos_gArea := as.numeric(V1)][, V1 := NULL]
#loop for north
for (i in 1:(length(east_atl_north_latitudes)-1)) {
#setting up extent for slicing by min and max longitudes, and i to i+1 latitudes
north_extent <- c(xmin(east_atl_spdf_nobuf_mask_1s), xmax(east_atl_spdf_nobuf_mask_1s), east_atl_north_latitudes[i], east_atl_north_latitudes[i+1])
#crop raster segement based on bin extent
segment_north <- crop(east_atl_spdf_nobuf_mask_1s, extent(north_extent))
#populate data table with latitudinal bin
east_atl_shelf_areas[i, "latitude_start"] <- east_atl_north_latitudes[i]
east_atl_shelf_areas[i, "latitude_end"] <- east_atl_north_latitudes[i+1]
if(all(is.na(values(segment_north)))) { #if there's no shelf area within a bin, all area = 0
east_atl_shelf_areas[i, "area_equalareaproj"] <- 0
east_atl_shelf_areas[i, "area_rasterarea"] <- 0
east_atl_shelf_areas[i, "area_rgeos_gArea"] <- 0
print(i)
} else { #if there is shelf area within the bin, calculate area of slice
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_north, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_north)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster) #in km^2
#populate data table with raster area
east_atl_shelf_areas[i, "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_north.sp <- rasterToPolygons(segment_north, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_north.sp.EA <- spTransform(segment_north.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_north.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with polygon area using raster calculation
east_atl_shelf_areas[i, "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_north.sp.EA)/1e6
#populate data table with polygon area using regeos calculation
east_atl_shelf_areas[i, "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24
[1] 25
[1] 26
[1] 27
[1] 28
[1] 29
[1] 30
[1] 31
[1] 32
[1] 33
[1] 34
[1] 35
[1] 36
[1] 37
[1] 38
[1] 39
[1] 40
[1] 41
#loop for south
for (i in 1:(length(east_atl_south_latitudes)-1)) {
south_extent <- c(xmin(east_atl_spdf_nobuf_mask_1s), xmax(east_atl_spdf_nobuf_mask_1s), east_atl_south_latitudes[i+1], east_atl_south_latitudes[i]) #order= xmin, xmax, ymin, ymax)
#raster segment
segment_south <- crop(east_atl_spdf_nobuf_mask_1s, extent(south_extent))
#add latitude bin info to data table
east_atl_shelf_areas[i+(length(east_atl_north_latitudes)-1), "latitude_start"] <- east_atl_south_latitudes[i]
east_atl_shelf_areas[i+(length(east_atl_north_latitudes)-1), "latitude_end"] <- east_atl_south_latitudes[i+1]
if(all(is.na(values(segment_south)))) { #if there's no shelf area within a bin, meaning there's no shelf area at that latitude
east_atl_shelf_areas[i+(length(east_atl_north_latitudes)-1), "area_equalareaproj"] <- 0
east_atl_shelf_areas[i+(length(east_atl_north_latitudes)-1), "area_rasterarea"] <- 0
east_atl_shelf_areas[i+(length(east_atl_north_latitudes)-1), "area_rgeos_gArea"] <- 0
print(i)
} else {
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_south, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_south)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster)
#populate data table with raster area
east_atl_shelf_areas[i+(length(east_atl_north_latitudes)-1), "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_south.sp <- rasterToPolygons(segment_south, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_south.sp.EA <- spTransform(segment_south.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_south.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with area of polygon from raster::area function
east_atl_shelf_areas[i+(length(east_atl_north_latitudes)-1), "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_south.sp.EA)/1e6
#populate data table from rgeos area calculation for projected polygon
east_atl_shelf_areas[i+(length(east_atl_north_latitudes)-1), "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
#compare raster:area calculation, to equal area still using raster::area function, to rgeos::gArea function for polygons
ggplot(data = east_atl_shelf_areas) +
geom_point(aes(x = latitude_start, y = area_rgeos_gArea), color = "purple", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_rasterarea), color = "darkgreen", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_equalareaproj), color = "red", size = 0.5) +
labs(x=paste0("Latitude ","\u00B0","E"), y = "Area km^2") +
theme_classic()

cor(east_atl_shelf_areas[,3:5], use = "complete.obs")
area_rasterarea area_equalareaproj area_rgeos_gArea
area_rasterarea 1.0000000 0.9999984 0.9999984
area_equalareaproj 0.9999984 1.0000000 1.0000000
area_rgeos_gArea 0.9999984 1.0000000 1.0000000
east_atl_shelf_areas[, percent_change := (area_rasterarea-data.table::shift(area_rasterarea, type = "lag"))/data.table::shift(area_rasterarea, type = "lag")][,area_1000s := area_rasterarea/1000]
east_atl_shelf_areas[,hemisphere := ifelse(latitude_end>0,"north","south")]
east_atl_shelf_areas[, change_above_2fold := ifelse((percent_change>=1 & percent_change<Inf), 1, ifelse(percent_change<=-0.5, -1, 0))]
east_atl_shelf_areas_highlight <- east_atl_shelf_areas[change_above_2fold != 0,]
east_atl_shelf_areas_stats <- table(east_atl_shelf_areas[,.(change_above_2fold, hemisphere)])
Model change for southern and northern hemisphere
#add mid latitude
east_atl_shelf_areas[,latitude_mid := abs((latitude_end+latitude_start)/2)]
east_atl_north_mod <- lm(data = east_atl_shelf_areas[hemisphere == "north"], area_rasterarea ~ latitude_mid)
summary(east_atl_north_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = east_atl_shelf_areas[hemisphere ==
"north"])
Residuals:
Min 1Q Median 3Q Max
-232911 -81720 -36339 78443 406088
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -102304.8 46235.0 -2.213 0.0328 *
latitude_mid 6586.0 976.7 6.743 4.83e-08 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 148000 on 39 degrees of freedom
Multiple R-squared: 0.5383, Adjusted R-squared: 0.5265
F-statistic: 45.47 on 1 and 39 DF, p-value: 4.833e-08
east_atl_south_mod <- lm(data = east_atl_shelf_areas[hemisphere == "south"], area_rasterarea ~ latitude_mid)
summary(east_atl_south_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = east_atl_shelf_areas[hemisphere ==
"south"])
Residuals:
Min 1Q Median 3Q Max
-14704 -6419 1874 5828 16485
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7823.1 4568.4 1.712 0.106
latitude_mid 614.7 219.9 2.796 0.013 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 9680 on 16 degrees of freedom
Multiple R-squared: 0.3282, Adjusted R-squared: 0.2862
F-statistic: 7.815 on 1 and 16 DF, p-value: 0.01296
Western Indian
west_ind_spdf_mask
north_extent <- c(xmin(west_ind_spdf_mask_1s), xmax(west_ind_spdf_mask_1s), 0, ymax(west_ind_spdf_mask_1s))
south_extent <- c(xmin(west_ind_spdf_mask_1s), xmax(west_ind_spdf_mask_1s), ymin(west_ind_spdf_mask_1s), 0)
#crop west_ind raster above and below 0
west_ind_spdf_shift_agg_north <- crop(west_ind_spdf_mask_1s, extent(north_extent))
west_ind_spdf_shift_agg_south <- crop(west_ind_spdf_mask_1s, extent(south_extent))
#unfortunately, I think I may have to just do this manually (ugly, I know)
#all chunks for west indian
west_ind_north_latitudes <- seq(0, ymax(west_ind_spdf_mask_1s), by = 2)
west_ind_south_latitudes <- seq(0, ymin(west_ind_spdf_mask_1s), by = -2)
#setup data table to populate in loop, subtracting one to allow for bins
west_ind_shelf_areas <- as.data.table(matrix(nrow = (length(west_ind_north_latitudes)-1+length(west_ind_south_latitudes)-1)))
west_ind_shelf_areas[, latitude_start := as.numeric(V1)][, latitude_end := as.numeric(V1)][, area_rasterarea := as.numeric(V1)][, area_equalareaproj := as.numeric(V1)][, area_rgeos_gArea := as.numeric(V1)][, V1 := NULL]
#loop for north
for (i in 1:(length(west_ind_north_latitudes)-1)) {
#setting up extent for slicing by min and max longitudes, and i to i+1 latitudes
north_extent <- c(xmin(west_ind_spdf_mask_1s), xmax(west_ind_spdf_mask_1s), west_ind_north_latitudes[i], west_ind_north_latitudes[i+1])
#crop raster segement based on bin extent
segment_north <- crop(west_ind_spdf_mask_1s, extent(north_extent))
#populate data table with latitudinal bin
west_ind_shelf_areas[i, "latitude_start"] <- west_ind_north_latitudes[i]
west_ind_shelf_areas[i, "latitude_end"] <- west_ind_north_latitudes[i+1]
if(all(is.na(values(segment_north)))) { #if there's no shelf area within a bin, all area = 0
west_ind_shelf_areas[i, "area_equalareaproj"] <- 0
west_ind_shelf_areas[i, "area_rasterarea"] <- 0
west_ind_shelf_areas[i, "area_rgeos_gArea"] <- 0
print(i)
} else { #if there is shelf area within the bin, calculate area of slice
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_north, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_north)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster) #in km^2
#populate data table with raster area
west_ind_shelf_areas[i, "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_north.sp <- rasterToPolygons(segment_north, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_north.sp.EA <- spTransform(segment_north.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_north.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with polygon area using raster calculation
west_ind_shelf_areas[i, "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_north.sp.EA)/1e6
#populate data table with polygon area using regeos calculation
west_ind_shelf_areas[i, "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
#loop for south
for (i in 1:(length(west_ind_south_latitudes)-1)) {
south_extent <- c(xmin(west_ind_spdf_mask_1s), xmax(west_ind_spdf_mask_1s), west_ind_south_latitudes[i+1], west_ind_south_latitudes[i]) #order= xmin, xmax, ymin, ymax)
#raster segment
segment_south <- crop(west_ind_spdf_mask_1s, extent(south_extent))
#add latitude bin info to data table
west_ind_shelf_areas[i+(length(west_ind_north_latitudes)-1), "latitude_start"] <- west_ind_south_latitudes[i]
west_ind_shelf_areas[i+(length(west_ind_north_latitudes)-1), "latitude_end"] <- west_ind_south_latitudes[i+1]
if(all(is.na(values(segment_south)))) { #if there's no shelf area within a bin, meaning there's no shelf area at that latitude
west_ind_shelf_areas[i+(length(west_ind_north_latitudes)-1), "area_equalareaproj"] <- 0
west_ind_shelf_areas[i+(length(west_ind_north_latitudes)-1), "area_rasterarea"] <- 0
west_ind_shelf_areas[i+(length(west_ind_north_latitudes)-1), "area_rgeos_gArea"] <- 0
print(i)
} else {
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_south, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_south)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster)
#populate data table with raster area
west_ind_shelf_areas[i+(length(west_ind_north_latitudes)-1), "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_south.sp <- rasterToPolygons(segment_south, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_south.sp.EA <- spTransform(segment_south.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_south.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with area of polygon from raster::area function
west_ind_shelf_areas[i+(length(west_ind_north_latitudes)-1), "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_south.sp.EA)/1e6
#populate data table from rgeos area calculation for projected polygon
west_ind_shelf_areas[i+(length(west_ind_north_latitudes)-1), "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
#compare raster:area calculation, to equal area still using raster::area function, to rgeos::gArea function for polygons
ggplot(data = west_ind_shelf_areas) +
geom_point(aes(x = latitude_start, y = area_rgeos_gArea), color = "purple", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_rasterarea), color = "darkgreen", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_equalareaproj), color = "red", size = 0.5) +
labs(x=paste0("Latitude ","\u00B0","E"), y = "Area km^2") +
theme_classic()

cor(west_ind_shelf_areas[,3:5], use = "complete.obs")
area_rasterarea area_equalareaproj area_rgeos_gArea
area_rasterarea 1.0000000 0.9999996 0.9999996
area_equalareaproj 0.9999996 1.0000000 1.0000000
area_rgeos_gArea 0.9999996 1.0000000 1.0000000
west_ind_shelf_areas[, percent_change := (area_rasterarea-data.table::shift(area_rasterarea, type = "lag"))/data.table::shift(area_rasterarea, type = "lag")][,area_1000s := area_rasterarea/1000]
west_ind_shelf_areas[,hemisphere := ifelse(latitude_end>0,"north","south")]
west_ind_shelf_areas[, change_above_2fold := ifelse((percent_change>=1 & percent_change<Inf), 1, ifelse(percent_change<=-0.5, -1, 0))]
west_ind_shelf_areas_highlight <- west_ind_shelf_areas[change_above_2fold != 0,]
west_ind_shelf_areas_stats <- table(west_ind_shelf_areas[,.(change_above_2fold, hemisphere)])
Model change for southern and northern hemisphere
#add mid latitude
west_ind_shelf_areas[,latitude_mid := abs((latitude_end+latitude_start)/2)]
west_ind_north_mod <- lm(data = west_ind_shelf_areas[hemisphere == "north"], area_rasterarea ~ latitude_mid)
summary(west_ind_north_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = west_ind_shelf_areas[hemisphere ==
"north"])
Residuals:
Min 1Q Median 3Q Max
-49907 -10358 -1891 14076 33614
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -837.4 12418.7 -0.067 0.947264
latitude_mid 3767.6 717.4 5.252 0.000156 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 24010 on 13 degrees of freedom
Multiple R-squared: 0.6797, Adjusted R-squared: 0.655
F-statistic: 27.58 on 1 and 13 DF, p-value: 0.0001564
west_ind_south_mod <- lm(data = west_ind_shelf_areas[hemisphere == "south"], area_rasterarea ~ latitude_mid)
summary(west_ind_south_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = west_ind_shelf_areas[hemisphere ==
"south"])
Residuals:
Min 1Q Median 3Q Max
-22996 -12112 -2283 6339 44624
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4120.2 8448.0 0.488 0.632
latitude_mid 700.9 406.6 1.724 0.104
Residual standard error: 17900 on 16 degrees of freedom
Multiple R-squared: 0.1566, Adjusted R-squared: 0.1039
F-statistic: 2.971 on 1 and 16 DF, p-value: 0.104
Eastern Indian
east_ind_spdf_mask_1s
north_extent <- c(xmin(east_ind_spdf_mask_1s), xmax(east_ind_spdf_mask_1s), 0, ymax(east_ind_spdf_mask_1s))
south_extent <- c(xmin(east_ind_spdf_mask_1s), xmax(east_ind_spdf_mask_1s), ymin(east_ind_spdf_mask_1s), 0)
#crop east_ind raster above and below 0
east_ind_spdf_shift_agg_north <- crop(east_ind_spdf_mask_1s, extent(north_extent))
east_ind_spdf_shift_agg_south <- crop(east_ind_spdf_mask_1s, extent(south_extent))
#unfortunately, I think I may have to just do this manually (ugly, I know)
#all chunks for east indian
east_ind_north_latitudes <- seq(0, ymax(east_ind_spdf_mask_1s), by = 2)
east_ind_south_latitudes <- seq(0, ymin(east_ind_spdf_mask_1s), by = -2)
#setup data table to populate in loop, subtracting one to allow for bins
east_ind_shelf_areas <- as.data.table(matrix(nrow = (length(east_ind_north_latitudes)-1+length(east_ind_south_latitudes)-1)))
east_ind_shelf_areas[, latitude_start := as.numeric(V1)][, latitude_end := as.numeric(V1)][, area_rasterarea := as.numeric(V1)][, area_equalareaproj := as.numeric(V1)][, area_rgeos_gArea := as.numeric(V1)][, V1 := NULL]
#loop for north
for (i in 1:(length(east_ind_north_latitudes)-1)) {
#setting up extent for slicing by min and max longitudes, and i to i+1 latitudes
north_extent <- c(xmin(east_ind_spdf_mask_1s), xmax(east_ind_spdf_mask_1s), east_ind_north_latitudes[i], east_ind_north_latitudes[i+1])
#crop raster segement based on bin extent
segment_north <- crop(east_ind_spdf_mask_1s, extent(north_extent))
#populate data table with latitudinal bin
east_ind_shelf_areas[i, "latitude_start"] <- east_ind_north_latitudes[i]
east_ind_shelf_areas[i, "latitude_end"] <- east_ind_north_latitudes[i+1]
if(all(is.na(values(segment_north)))) { #if there's no shelf area within a bin, all area = 0
east_ind_shelf_areas[i, "area_equalareaproj"] <- 0
east_ind_shelf_areas[i, "area_rasterarea"] <- 0
east_ind_shelf_areas[i, "area_rgeos_gArea"] <- 0
print(i)
} else { #if there is shelf area within the bin, calculate area of slice
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_north, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_north)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster) #in km^2
#populate data table with raster area
east_ind_shelf_areas[i, "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_north.sp <- rasterToPolygons(segment_north, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_north.sp.EA <- spTransform(segment_north.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_north.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with polygon area using raster calculation
east_ind_shelf_areas[i, "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_north.sp.EA)/1e6
#populate data table with polygon area using regeos calculation
east_ind_shelf_areas[i, "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
#loop for south
for (i in 1:(length(east_ind_south_latitudes)-1)) {
south_extent <- c(xmin(east_ind_spdf_mask_1s), xmax(east_ind_spdf_mask_1s), east_ind_south_latitudes[i+1], east_ind_south_latitudes[i]) #order= xmin, xmax, ymin, ymax)
#raster segment
segment_south <- crop(east_ind_spdf_mask_1s, extent(south_extent))
#add latitude bin info to data table
east_ind_shelf_areas[i+(length(east_ind_north_latitudes)-1), "latitude_start"] <- east_ind_south_latitudes[i]
east_ind_shelf_areas[i+(length(east_ind_north_latitudes)-1), "latitude_end"] <- east_ind_south_latitudes[i+1]
if(all(is.na(values(segment_south)))) { #if there's no shelf area within a bin, meaning there's no shelf area at that latitude
east_ind_shelf_areas[i+(length(east_ind_north_latitudes)-1), "area_equalareaproj"] <- 0
east_ind_shelf_areas[i+(length(east_ind_north_latitudes)-1), "area_rasterarea"] <- 0
east_ind_shelf_areas[i+(length(east_ind_north_latitudes)-1), "area_rgeos_gArea"] <- 0
print(i)
} else {
#raster area calculation
#get sizes of all cells in raster [km2]
cell_size_raster<-area(segment_south, na.rm=TRUE, weights=FALSE)
#delete NAs from vector of all raster cells
cell_size_raster<-cell_size_raster[!is.na(segment_south)]
#compute area of all cells in geo_raster
#full area <- total # grid cells * median cell area (using median cares less about extreme values)
segment_area_raster <- length(cell_size_raster)*median(cell_size_raster)
#populate data table with raster area
east_ind_shelf_areas[i+(length(east_ind_north_latitudes)-1), "area_rasterarea"] <- segment_area_raster
#convert to spatial polygons to check area calculations
#convert segment from raster to polygon, each cell from the raster is an independent polygon, (dissolve means all cells with a value of 1 are a single polygon if connected)
segment_south.sp <- rasterToPolygons(segment_south, dissolve = T)
# If x is a SpatialPolygons* object: area of each spatial object in squared meters if the CRS is longitude/latitude, or in squared map units (typically meter)
#project to equal earth area projection
segment_south.sp.EA <- spTransform(segment_south.sp, CRSobj = equalareaprojection)
#calculate area of the spatial object in m^2
polygon_size.sp <- area(segment_south.sp.EA)
#convert from m^2 to km^2
segment_area_equalarea <- polygon_size.sp/1e6
#populate data table with area of polygon from raster::area function
east_ind_shelf_areas[i+(length(east_ind_north_latitudes)-1), "area_equalareaproj"] <- segment_area_equalarea
#and then plain and simple also using rgeos::gArea
area_rgeos_gArea <- gArea(segment_south.sp.EA)/1e6
#populate data table from rgeos area calculation for projected polygon
east_ind_shelf_areas[i+(length(east_ind_north_latitudes)-1), "area_rgeos_gArea"] <- area_rgeos_gArea
print(i)
}
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf
[1] 24
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf
[1] 25
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf
[1] 26
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf
[1] 27
#compare raster:area calculation, to equal area still using raster::area function, to rgeos::gArea function for polygons
ggplot(data = east_ind_shelf_areas) +
geom_point(aes(x = latitude_start, y = area_rgeos_gArea), color = "purple", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_rasterarea), color = "darkgreen", size = 0.5) +
geom_point(aes(x = latitude_start, y = area_equalareaproj), color = "red", size = 0.5) +
labs(x=paste0("Latitude ","\u00B0","E"), y = "Area km^2") +
theme_classic()

cor(east_ind_shelf_areas[,3:5], use = "complete.obs")
area_rasterarea area_equalareaproj area_rgeos_gArea
area_rasterarea 1.0000000 0.9999981 0.9999981
area_equalareaproj 0.9999981 1.0000000 1.0000000
area_rgeos_gArea 0.9999981 1.0000000 1.0000000
east_ind_shelf_areas[, percent_change := (area_rasterarea-data.table::shift(area_rasterarea, type = "lag"))/data.table::shift(area_rasterarea, type = "lag")][,area_1000s := area_rasterarea/1000]
east_ind_shelf_areas[,hemisphere := ifelse(latitude_end>0,"north","south")]
east_ind_shelf_areas[, change_above_2fold := ifelse((percent_change>=1 & percent_change<Inf), 1, ifelse(percent_change<=-0.5, -1, 0))]
east_ind_shelf_areas_highlight <- east_ind_shelf_areas[change_above_2fold != 0,]
east_ind_shelf_areas_stats <- table(east_ind_shelf_areas[,.(change_above_2fold,hemisphere)])
Model change for southern and northern hemisphere
#add mid latitude
east_ind_shelf_areas[,latitude_mid := abs((latitude_end+latitude_start)/2)]
east_ind_north_mod <- lm(data = east_ind_shelf_areas[hemisphere == "north"], area_rasterarea ~ latitude_mid)
summary(east_ind_north_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = east_ind_shelf_areas[hemisphere ==
"north"])
Residuals:
Min 1Q Median 3Q Max
-34517 -14936 -1836 17006 39357
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 42779 15428 2.773 0.0217 *
latitude_mid 1238 1216 1.018 0.3353
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 25500 on 9 degrees of freedom
Multiple R-squared: 0.1032, Adjusted R-squared: 0.003607
F-statistic: 1.036 on 1 and 9 DF, p-value: 0.3353
east_ind_south_mod <- lm(data = east_ind_shelf_areas[hemisphere == "south"], area_rasterarea ~ latitude_mid)
summary(east_ind_south_mod)
Call:
lm(formula = area_rasterarea ~ latitude_mid, data = east_ind_shelf_areas[hemisphere ==
"south"])
Residuals:
Min 1Q Median 3Q Max
-44084 -34503 -19335 22903 130164
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 54193.3 18114.7 2.992 0.00616 **
latitude_mid -410.2 581.1 -0.706 0.48677
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 47040 on 25 degrees of freedom
Multiple R-squared: 0.01954, Adjusted R-squared: -0.01968
F-statistic: 0.4983 on 1 and 25 DF, p-value: 0.4868
Plots of latitude versus habitat availability
Include trend lines only for those with significant coefficients between latitude start and area^2 NOT: - East Indian Southern and Northern - East Pacific Southern - West Indian Southern
##east indian
#simulate data to plot trendline
east_ind_north_data <- data.table(latitude_mid = unique(east_ind_shelf_areas[latitude_end>0,]$latitude_mid))
east_ind_north_data[,area_1000s := east_ind_north_mod$coefficients[[1]]/1000+east_ind_north_mod$coefficients[[2]]/1000*latitude_mid]
east_ind_south_data <- data.table(latitude_mid = unique(east_ind_shelf_areas[latitude_end <0,]$latitude_mid))
east_ind_south_data[,area_1000s := east_ind_south_mod$coefficients[[1]]/1000+east_ind_south_mod$coefficients[[2]]/1000*latitude_mid]
#east indian
(area_latitude_east_ind <- ggplot() +
# geom_line(data = east_ind_north_data, aes(x=latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") + not significant
# geom_line(data = east_ind_south_data, aes(x=latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") + not significant
geom_point(data = east_ind_shelf_areas, aes(x=latitude_start, y=area_1000s), shape =18, size = 0.7) +
geom_line(data = east_ind_shelf_areas, aes(x=latitude_start, y=area_1000s), size = 0.7) +
geom_rug(data = east_ind_shelf_areas_highlight, aes(x = latitude_start, color = as.factor(change_above_2fold))) +
labs(x=paste0("Latitude ","\u00B0","E"), y = expression(paste("Area (1000s of ", km^{2},")"))) +
scale_color_viridis_d(option = "A", begin = 0.3, end = 0.7, name = "Associated Change\nin Shelf Area", labels = c("Contraction", "Expansion")) +
##annotate("text", x =22, y = 70000, label = "Eastern Indian Ocean") +
geom_vline(xintercept = 0) +
xlim(min(east_ind_shelf_areas$latitude_end), max(east_ind_shelf_areas$latitude_end)) +
coord_flip() +
theme_classic() +
theme(plot.margin = margin(10, 40, 10, 10)))
ggsave(area_latitude_east_ind, filename = "area_latitude_east_ind_2degrees.jpg", height = 4, units = c("in"))
Saving 7.29 x 4 in image

##west indian
west_ind_north_data <- data.table(latitude_mid = unique(west_ind_shelf_areas[latitude_end>0,]$latitude_mid))
west_ind_north_data[,area_1000s := west_ind_north_mod$coefficients[[1]]/1000+west_ind_north_mod$coefficients[[2]]/1000*latitude_mid]
west_ind_south_data <- data.table(latitude_mid = unique(west_ind_shelf_areas[latitude_end <0,]$latitude_mid))
west_ind_south_data[,area_1000s := west_ind_south_mod$coefficients[[1]]/1000+west_ind_south_mod$coefficients[[2]]/1000*latitude_mid]
(area_latitude_west_ind <- ggplot() +
geom_line(data = west_ind_north_data, aes(x=latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") +
# geom_line(data = west_ind_south_data, aes(x=latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") + not significant
geom_point(data = west_ind_shelf_areas, aes(x=latitude_start, y=area_1000s), shape =18, size = 0.7) +
geom_line(data = west_ind_shelf_areas, aes(x=latitude_start, y=area_1000s), size = 0.7) +
geom_rug(data = west_ind_shelf_areas_highlight, aes(x = latitude_start, color = as.factor(change_above_2fold))) +
labs(x=paste0("Latitude ","\u00B0","E"), y = expression(paste("Area (1000s of ", km^{2},")"))) +
scale_color_viridis_d(option = "A", begin = 0.3, end = 0.7, name = "Associated Change\nin Shelf Area", labels = c("Contraction", "Expansion")) +
#annotate("text", x = 30, y = 33000, label = "Western Indian Ocean") +
geom_vline(xintercept = 0) +
xlim(min(west_ind_shelf_areas$latitude_end), max(west_ind_shelf_areas$latitude_end)) +
coord_flip() +
theme_classic() +
theme(plot.margin = margin(10, 40, 10, 10)))
ggsave(area_latitude_west_ind, filename = "area_latitude_west_ind_2degrees.jpg", height = 4, units = c("in"))
Saving 7.29 x 4 in image
west_atl_north_data <- data.table(latitude_mid = unique(west_atl_shelf_areas[latitude_end>0,]$latitude_mid))
west_atl_north_data[,area_1000s := west_atl_north_mod$coefficients[[1]]/1000+west_atl_north_mod$coefficients[[2]]/1000*latitude_mid]
west_atl_south_data <- data.table(latitude_mid = unique(west_atl_shelf_areas[latitude_end <0,]$latitude_mid))
west_atl_south_data[,area_1000s := west_atl_south_mod$coefficients[[1]]/1000+west_atl_south_mod$coefficients[[2]]/1000*latitude_mid]
area_latitude_west_atl <- ggplot() +
geom_line(data = west_atl_north_data, aes(x=latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") +
geom_line(data = west_atl_south_data, aes(x=-latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") +
geom_point(data = west_atl_shelf_areas, aes(x=latitude_start, y=area_1000s), shape =18, size = 0.7) +
geom_line(data = west_atl_shelf_areas, aes(x=latitude_start, y=area_1000s), size = 0.7) +
geom_rug(data = west_atl_shelf_areas_highlight, aes(x = latitude_start, color = as.factor(change_above_2fold))) +
labs(x=paste0("Latitude ","\u00B0","E"), y = expression(paste("Area (1000s of ", km^{2},")"))) +
scale_color_viridis_d(option = "A", begin = 0.3, end = 0.7, name = "Associated Change\nin Shelf Area", labels = c("Contraction", "Expansion")) +
#annotate("text", x = 80, y = 120000, label = "Western Atlantic Ocean") +
geom_vline(xintercept = 0) +
xlim(min(west_atl_shelf_areas$latitude_end), max(west_atl_shelf_areas$latitude_end)) +
coord_flip() +
theme_classic() +
theme(plot.margin = margin(10, 40, 10, 10))
ggsave(area_latitude_west_atl, filename = "area_latitude_west_atl_2degrees.jpg", height = 4, units = c("in"))
Saving 7.29 x 4 in image
east_atl_north_data <- data.table(latitude_mid = unique(east_atl_shelf_areas[latitude_end>0,]$latitude_mid))
east_atl_north_data[,area_1000s := east_atl_north_mod$coefficients[[1]]/1000+east_atl_north_mod$coefficients[[2]]/1000*latitude_mid]
east_atl_south_data <- data.table(latitude_mid = unique(east_atl_shelf_areas[latitude_end <0,]$latitude_mid))
east_atl_south_data[,area_1000s := east_atl_south_mod$coefficients[[1]]/1000+east_atl_south_mod$coefficients[[2]]/1000*latitude_mid]
area_latitude_east_atl <- ggplot() +
geom_line(data = east_atl_north_data, aes(x=latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") +
geom_line(data = east_atl_south_data, aes(x=-latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") +
geom_point(data = east_atl_shelf_areas, aes(x=latitude_start, y=area_1000s), shape =18, size = 0.7) +
geom_line(data = east_atl_shelf_areas, aes(x=latitude_start, y=area_1000s), size = 0.7) +
geom_rug(data = east_atl_shelf_areas_highlight, aes(x = latitude_start, color = as.factor(change_above_2fold))) +
labs(x=paste0("Latitude ","\u00B0","E"), y = expression(paste("Area (1000s of ", km^{2},")"))) +
scale_color_viridis_d(option = "A", begin = 0.3, end = 0.7, name = "Associated Change\nin Shelf Area", labels = c("Contraction", "Expansion")) +
#annotate("text", x = 82.5, y = 110000, label = "Eastern Atlantic Ocean") +
geom_vline(xintercept = 0) +
xlim(min(east_atl_shelf_areas$latitude_end), max(east_atl_shelf_areas$latitude_end)) +
coord_flip() +
theme_classic() +
theme(plot.margin = margin(10, 40, 10, 10))
ggsave(area_latitude_east_atl, filename = "area_latitude_east_atl_2degrees.jpg", height = 4, units = c("in"))
Saving 7.29 x 4 in image
east_pac_north_data <- data.table(latitude_mid = unique(east_pac_shelf_areas[latitude_end>0,]$latitude_mid))
east_pac_north_data[,area_1000s := east_pac_north_mod$coefficients[[1]]/1000+east_pac_north_mod$coefficients[[2]]/1000*latitude_mid]
east_pac_south_data <- data.table(latitude_mid = unique(east_pac_shelf_areas[latitude_end <0,]$latitude_mid))
east_pac_south_data[,area_1000s := east_pac_south_mod$coefficients[[1]]/1000+east_pac_south_mod$coefficients[[2]]/1000*latitude_mid]
area_latitude_east_pac <- ggplot() +
geom_line(data = east_pac_north_data, aes(x=latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") +
# geom_line(data = east_pac_south_data, aes(x=latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") + not significant
geom_point(data = east_pac_shelf_areas, aes(x=latitude_start, y=area_1000s), shape =18, size = 0.7) +
geom_line(data = east_pac_shelf_areas, aes(x=latitude_start, y=area_1000s), size = 0.7) +
geom_rug(data = east_pac_shelf_areas_highlight, aes(x = latitude_start, color = as.factor(change_above_2fold))) +
labs(x=paste0("Latitude ","\u00B0","E"), y = expression(paste("Area (1000s of ", km^{2},")"))) +
scale_color_viridis_d(option = "A", begin = 0.3, end = 0.7, name = "Associated Change\nin Shelf Area", labels = c("Contraction", "Expansion")) +
#annotate("text", x = 80, y = 130000, label = "Eastern Pacific Ocean") +
geom_vline(xintercept = 0) +
xlim(min(east_pac_shelf_areas$latitude_end), max(east_pac_shelf_areas$latitude_end)) +
coord_flip() +
theme_classic() +
theme(plot.margin = margin(10, 40, 10, 10))
ggsave(area_latitude_east_pac, filename = "area_latitude_east_pac_2degrees.jpg", height = 4, units = c("in"))
Saving 7.29 x 4 in image

west_pac_north_data <- data.table(latitude_mid = unique(west_pac_shelf_areas[latitude_end>0,]$latitude_mid))
west_pac_north_data[,area_1000s := west_pac_north_mod$coefficients[[1]]/1000+west_pac_north_mod$coefficients[[2]]/1000*latitude_mid]
west_pac_south_data <- data.table(latitude_mid = unique(west_pac_shelf_areas[latitude_end <0,]$latitude_mid))
west_pac_south_data[,area_1000s := west_pac_south_mod$coefficients[[1]]/1000+west_pac_south_mod$coefficients[[2]]/1000*latitude_mid]
(area_latitude_west_pac <- ggplot() +
geom_line(data = west_pac_north_data, aes(x=latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") +
geom_line(data = west_pac_south_data, aes(x=-latitude_mid, y = area_1000s), color = "gray60", size = 0.8, linetype = "longdash") +
geom_point(data = west_pac_shelf_areas, aes(x=latitude_start, y=area_1000s), shape =18, size = 0.7) +
geom_line(data = west_pac_shelf_areas, aes(x=latitude_start, y=area_1000s), size = 0.7) +
geom_rug(data = west_pac_shelf_areas_highlight, aes(x = latitude_start, color = as.factor(change_above_2fold))) +
labs(x=paste0("Latitude ","\u00B0","E"), y = expression(paste("Area (1000s of ", km^{2},")"))) +
scale_color_viridis_d(option = "A", begin = 0.3, end = 0.7, name = "Associated Change\nin Shelf Area", labels = c("Contraction", "Expansion")) +
#annotate("text", x = 90, y = 130000, label = "Western Pacific Ocean") +
geom_vline(xintercept = 0) +
xlim(min(west_pac_shelf_areas$latitude_end), max(west_pac_shelf_areas$latitude_end)) +
coord_flip() +
theme_classic() +
theme(plot.margin = margin(10, 40, 10, 10)))
ggsave(area_latitude_west_pac, filename = "area_latitude_west_pac_2degrees.jpg", height = 4, units = c("in"))
Saving 7.29 x 4 in image

Dumby graph to just make additional legend component
legend_lat_area_regression <- get_legend(
ggplot() +
geom_line(data = west_pac_north_data, aes(x=latitude_mid, y = area_1000s, color = "value"), size = 0.8, linetype = "longdash") +
scale_color_manual(values = "gray60", labels = "Area ~ Latitude\nLinear Regression") +
coord_flip() +
theme_classic() +
theme(plot.margin = margin(10, 40, 10, 10), legend.title = element_blank()))
save(legend_lat_area_regression, here::here("Figures", "Figure3_5", "legend_lat_area_regression.RData"))
Error in save(legend_lat_area_regression, here::here("Figures", "Figure3_5", :
object ‘here::here("Figures", "Figure3_5", "legend_lat_area_regression.RData")’ not found
How many experience ‘significant’ changes in habitat (at least -50% or +100% change from one bin to another) I will go with IUCN 50% loss -> vulnerable species designation.
Bin shifts –> contractions (loss of 50%) versus expansions (gain of 200%) versus neutral
library(ggrepel)
#call all objects in environment with "stats" string
stats_string<-grep("areas_stats",names(.GlobalEnv),value=TRUE)
stats_string_list<-do.call("list",mget(stats_string))
names(stats_string_list) #check
[1] "east_ind_shelf_areas_stats" "west_pac_shelf_areas_stats" "east_pac_shelf_areas_stats"
[4] "west_atl_shelf_areas_stats" "west_ind_shelf_areas_stats" "east_atl_shelf_areas_stats"
#rownames
stats_string_rownames <- rep(names(stats_string_list), each = 3)
stats_string_type <- rep(c("contraction", "neutral", "expansion"),times = 6)
#check <- c("Eastern Indian Ocean" ,"Western Pacific Ocean" ,"Eastern Pacific Ocean" ,"Western Atlantic Ocean" ,"Western Indian Ocean" ,"Eastern Atlantic Ocean")
significant_changes <- as.data.table(rbind(stats_string_list[[1]],stats_string_list[[2]],stats_string_list[[3]],stats_string_list[[4]],stats_string_list[[5]],stats_string_list[[6]]))
significant_changes[, region := stats_string_rownames][,type := stats_string_type]
#melt to plot
significant_changes.long <- melt(significant_changes, id.vars = c("region","type"), variable.name = "hemisphere", measure.vars = c("north", "south"))
#calculate percentages
significant_changes.long[,total_bins_hemisphere := sum(value),.(region, hemisphere)][,total_bins_region := sum(value),region][,percent_by_hemisphere := round(sum(value)/total_bins_hemisphere*100,2),.(region,hemisphere,type)][,percent_by_region := round(sum(value)/total_bins_region*100,2), .(region,type)]
significant_changes.long[,type := factor(type, levels = c("contraction","neutral","expansion"))][,region_names := factor(region, levels = c("east_atl_shelf_areas_stats", "west_atl_shelf_areas_stats", "east_ind_shelf_areas_stats", "west_ind_shelf_areas_stats" ,"east_pac_shelf_areas_stats" ,"west_pac_shelf_areas_stats"), labels = c("East Atlantic", "West Atlantic", "East Indian", "West Indian" ,"East Pacific" ,"West Pacific"))][,hemisphere := factor(hemisphere,levels = c("north","south"), labels = c("North","South"))]
blank_theme <- theme_minimal()+
theme(
axis.title.x = element_blank(),
axis.title.y = element_blank(),
panel.border = element_blank(),
panel.grid=element_blank(),
axis.ticks = element_blank(),
plot.title=element_text(size=14, face="bold")
)
#viridis magma palette, Contractions [1], expansions[2])
viridis_exp_cont <-viridis_pal(begin = 0.3, end = 0.7, option = "A")(2)
#by hemisphere
ggplot(data = significant_changes.long, aes(x="", y = percent_by_hemisphere, fill = type)) +
geom_bar(stat = "identity", width = 1, alpha = 0.8) +
coord_polar("y", start=0) +
scale_fill_manual(values = c(viridis_exp_cont[1], "grey94", viridis_exp_cont[2]), name = "Shelf Area Change", labels = c("Contraction", "Neither", "Expansion")) +
facet_grid(hemisphere~region_names) +
geom_text_repel(aes(label = paste0(round(percent_by_hemisphere,1),"%")), size = 1, fontface = "bold", position = position_stack(vjust = 0.3)) +
scale_x_discrete(expand = c(0,0)) +
blank_theme +
theme(axis.text.x=element_blank(),
strip.text = element_text(size = 7),
legend.title = element_text(size = 9),
legend.text = element_text(size = 7)) +
guides(shape = guide_legend(override.aes = list(size = 0.5)),
fill = guide_legend(override.aes = list(size = 0.5)))
ggsave(filename = "figure6_habitatloss_gain_2fold_byhemisphere.jpg", height = 2, width = 6, unit = "in")
ggsave(filename = "figure6_habitatloss_gain_2fold_byhemisphere.eps", height = 2, width = 6, unit = "in")

#by region (hemisphere's compressed)
ggplot(data = unique(significant_changes.long[,.(region_names,type,percent_by_region)]), aes(x="", y = percent_by_region, fill = type)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y", start=0) +
scale_fill_manual(values = c(viridis_exp_cont[1], "grey94", viridis_exp_cont[2]), name = "Shelf Area Change", labels = c("Contraction", "Neither", "Expansion")) +
facet_wrap(~region_names) +
geom_text(aes(label = paste0(percent_by_region,"%")), position = position_stack(vjust = 0.1), size = 2) +
scale_x_discrete(expand = c(0,0)) +
blank_theme +
theme(axis.text.x=element_blank())
ggsave(filename = "figure6_habitatloss_gain_2fold_byregion.jpg", height = 4, width = 8, unit = "in")
ggsave(filename = "figure6_habitatloss_gain_2fold_byregion.eps", height = 4, width = 8, unit = "in")

NA
NA
Now, I should make maps for each of these regions
Used https://gist.github.com/valentinitnelav/c7598fcfc8e53658f66feea9d3bafb40 for instructions
library(ggspatial)
world <- ne_countries(scale = "medium", returnclass = "sf")
# ~~~~~~~~~~~ Download shapefile from www.naturalearthdata.com ~~~~~~~~~~~ #
# Download countries data
#download.file(url = "http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/110m/cultural/ne_110m_admin_0_countries.zip",
# destfile = "ne_110m_admin_0_countries.zip")
# unzip the shapefile in the directory mentioned with "exdir" argument
#unzip(zipfile="ne_110m_admin_0_countries.zip", exdir = "ne_110m_admin_0_countries")
# delete the zip file
#file.remove("ne_110m_admin_0_countries.zip")
# read the shapefile with readOGR from rgdal package
NE_countries <- readOGR(dsn = "ne_110m_admin_0_countries", layer = "ne_110m_admin_0_countries")
OGR data source with driver: ESRI Shapefile
Source: "/Users/zoekitchel/Documents/grad school/Rutgers/Repositories/shelf_habitat_distribution/ne_110m_admin_0_countries", layer: "ne_110m_admin_0_countries"
with 177 features
It has 94 fields
Integer64 fields read as strings: POP_EST NE_ID
class(NE_countries) # is a SpatialPolygonsDataFrame object
[1] "SpatialPolygonsDataFrame"
attr(,"package")
[1] "sp"
# ~~~~~~~~~~~ Split world map by "split line" ~~~~~~~~~~~ #
# shift central/prime meridian towards west – positive values only
shift <- 180 +30
# create "split line" to split country polygons
WGS84 <- CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0")
split.line <- SpatialLines(list(Lines(list(Line(cbind(180-shift,c(-90,90)))), ID="line")),
proj4string=WGS84)
# NOTE - in case of TopologyException' errors when intersecting line with country polygons,
# apply the gBuffer solution suggested at:
# http://gis.stackexchange.com/questions/163445/r-solution-for-topologyexception-input-geom-1-is-invalid-self-intersection-er
NE_countries <- gBuffer(NE_countries, byid=TRUE, width=0)
Spatial object is not projected; GEOS expects planar coordinates
# intersecting line with country polygons
line.gInt <- gIntersection(split.line, NE_countries)
spgeom1 and spgeom2 have different proj4 strings
# create a very thin polygon (buffer) out of the intersecting "split line"
bf <- gBuffer(line.gInt, byid=TRUE, width=0.000001)
Spatial object is not projected; GEOS expects planar coordinates
# split country polygons using intersecting thin polygon (buffer)
NE_countries.split <- gDifference(NE_countries, bf, byid=TRUE)
spgeom1 and spgeom2 have different proj4 strings
# plot(NE_countries.split) # check map
class(NE_countries.split) # is a SpatialPolygons object
[1] "SpatialPolygons"
attr(,"package")
[1] "sp"
# ~~~~~~~~~~~ Create graticules ~~~~~~~~~~~ #
# create a bounding box - world extent
b.box <- as(raster::extent(-180, 180, -90, 90), "SpatialPolygons")
# assign CRS to box
proj4string(b.box) <- WGS84
# create graticules/grid lines from box
grid <- gridlines(b.box,
easts = seq(from=-180, to=180, by=20),
norths = seq(from=-90, to=90, by=10))
# create labels for graticules
grid.lbl <- labels(grid, side = 1:4)
# transform labels from SpatialPointsDataFrame to a data table that ggplot can use
grid.lbl.DT <- data.table(grid.lbl@coords, grid.lbl@data)
# prepare labels with regular expression:
# - delete unwanted labels
grid.lbl.DT[, labels := gsub(pattern="180\\*degree|90\\*degree\\*N|90\\*degree\\*S", replacement="", x=labels)]
# - replace pattern "*degree" with "°" (* needs to be escaped with \\)
grid.lbl.DT[, lbl := gsub(pattern="\\*degree", replacement="°", x=labels)]
# - delete any remaining "*"
grid.lbl.DT[, lbl := gsub(pattern="*\\*", replacement="", x=lbl)]
# adjust coordinates of labels so that they fit inside the globe
grid.lbl.DT[, long := ifelse(coords.x1 %in% c(-180,180), coords.x1*175/180, coords.x1)]
grid.lbl.DT[, lat := ifelse(coords.x2 %in% c(-90,90), coords.x2*82/90, coords.x2)]
# ~~~~~~~~~~~ Prepare data for ggplot, shift & project coordinates ~~~~~~~~~~~ #
# give the PORJ.4 string for Eckert IV projection ( changed to different projection, "+proj=eck4 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs" for eckert)
PROJ <- "+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0"
# transform graticules from SpatialLines to a data table that ggplot can use
grid.DT <- data.table(map_data(SpatialLinesDataFrame(sl=grid,
data=data.frame(1:length(grid)),
match.ID = FALSE)))
database does not (uniquely) contain the field 'name'.
# project coordinates
# assign matrix of projected coordinates as two columns in data table
grid.DT[, c("X","Y") := data.table(project(cbind(long, lat), proj=PROJ))]
# project coordinates of labels
grid.lbl.DT[, c("X","Y") := data.table(project(cbind(long, lat), proj=PROJ))]
# transform split country polygons in a data table that ggplot can use
Country.DT_shift <- data.table(map_data(as(NE_countries.split, "SpatialPolygonsDataFrame")))
database does not (uniquely) contain the field 'name'.
Country.DT <- data.table(map_data(as(NE_countries, "SpatialPolygonsDataFrame")))
# Shift coordinates
Country.DT_shift[, long.new := long + shift]
Country.DT_shift[, long.new := ifelse(long.new > 180, long.new-360, long.new)]
# project coordinates
Country.DT[, c("X","Y") := data.table(project(cbind(long, lat), proj=PROJ))]
Country.DT_shift[, c("X","Y") := data.table(project(cbind(long.new, lat), proj=PROJ))]
# ~~~~~~~~~~~ Plot map ~~~~~~~~~~~ #
ggplot() +
# add projected countries
geom_polygon(data = Country.DT_shift,
aes(x = long.new+150, y = lat, group = group),
colour = "gray70",
fill = "gray90",
size = 0.25) +
# add graticules
geom_path(data = grid.DT,
aes(x = X, y = Y, group = group),
linetype = "dotted", colour = "grey50", size = .25) +
# add a bounding box (select graticules at edges)
geom_path(data = grid.DT[(long %in% c(-180,180) & region == "NS")
|(long %in% c(-180,180) & lat %in% c(-90,90) & region == "EW")],
aes(x = X, y = Y, group = group),
linetype = "solid", colour = "black", size = .3) +
# add graticule labels
geom_text(data = grid.lbl.DT, # latitude
aes(x = X, y = Y, label = lbl),
colour = "grey50", size = 2) +
# ensures that one unit on the x-axis is the same length as one unit on the y-axis
coord_equal() + # same as coord_fixed(ratio = 1)
# set empty theme
theme_void()

region_map_legend <- get_legend(region_maps[[6]])
range backtransformation not implemented in this coord; results may be wrong.
Combining plots
region_maps: “west_pac_spdf_shift”, “east_pac_spdf_shift”, “west_atl_spdf”, “west_ind_spdf”, “east_atl_spdf_nobuf”, “east_ind_spdf”
Plots of area versus latitude area_latitude_east_pac area_latitude_west_pac area_latitude_east_atl area_latitude_west_atl area_latitude_east_ind area_latitude_west_ind
Now, combine plots
library(egg)
library(ggpubr)
Attaching package: ‘ggpubr’
The following object is masked from ‘package:egg’:
ggarrange
The following object is masked from ‘package:raster’:
rotate
#west pacific
(west_pacific_merge_map_plot <- egg::ggarrange(region_maps[[1]],
area_latitude_west_pac
+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank() ),
nrow = 1,
top = T,
widths = c(2,1)))
range backtransformation not implemented in this coord; results may be wrong.



ggsave(plot = west_pacific_merge_map_plot, filename = "west_pacific_merge_map_plot_2degrees.jpg", width = 7, height = 3, units = "in")
#east pacific
(east_pacific_merge_map_plot <- egg::ggarrange(region_maps[[2]],
area_latitude_east_pac
+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank() )
,
nrow = 1,
top = T
,
widths = c(2,1)
))
range backtransformation not implemented in this coord; results may be wrong.


ggsave(plot = east_pacific_merge_map_plot, filename = "east_pacific_merge_map_plot_2degrees.jpg", width = 7, height = 3, units = "in")
#both together
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:ggpubr’:
get_legend
figure_3_pacific_merge_top <- egg::ggarrange(region_maps[[1]] + theme(axis.title.x = element_blank(), plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
area_latitude_west_pac +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
axis.title.x = element_blank(),
legend.position = "none",
plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
nrow = 1, ncol = 2, top = T, bottom = T, widths = c(1.5,1))
range backtransformation not implemented in this coord; results may be wrong.

figure_3_pacific_merge_bottom <- egg::ggarrange(region_maps[[2]] + theme(plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
area_latitude_east_pac +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
legend.position = "none",
plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
nrow = 1, ncol = 2, top = T, bottom = T, widths = c(1.5,1))
range backtransformation not implemented in this coord; results may be wrong.
legend <- get_legend(area_latitude_east_pac)
figure_3_pacific_merge <- plot_grid(figure_3_pacific_merge_top, figure_3_pacific_merge_bottom, ncol = 1, nrow = 2, labels = c("a.","b."), axis = "l", align = "v", hjust = -7, rel_heights = c(1,1.5))
figure_3_pacific_merge_legend <- plot_grid(figure_3_pacific_merge, legend, ncol = 2, nrow = 1, rel_widths = c(4,1))
ggsave(figure_3_pacific_merge_legend, path = here::here("Figures", "Figure3_5"), filename = "Figure3_Pacific_latitude_area.jpg", height = 4, width = 6, unit = "in")

#east atlantic
(east_atlantic_merge_map_plot <- egg::ggarrange(region_maps[[5]],
area_latitude_east_atl
+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank() )
,
nrow = 1,
top = T,
widths = c(2,1)))
range backtransformation not implemented in this coord; results may be wrong.



ggsave(plot = east_atlantic_merge_map_plot, filename = "east_atlantic_merge_map_plot_2degrees.jpg", width = 7, height = 3, units = "in")
#west atlantic
(west_atlantic_merge_map_plot <- egg::ggarrange(region_maps[[3]],
area_latitude_west_atl
+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank() )
,
nrow = 1,
top = T,
widths = c(2,1)))
range backtransformation not implemented in this coord; results may be wrong.


ggsave(plot = west_atlantic_merge_map_plot, filename = "west_atlantic_merge_map_plot_2degrees.jpg", width = 7, height = 3, units = "in")
#both together
library(cowplot)
figure_4_atlantic_merge_top <- egg::ggarrange(region_maps[[3]] + theme(axis.title.x = element_blank(), plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
area_latitude_west_atl +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
axis.title.x = element_blank(),
legend.position = "none",
plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
nrow = 1, ncol = 2, top = T, bottom = T, widths = c(1.5,1))
range backtransformation not implemented in this coord; results may be wrong.

figure_4_atlantic_merge_bottom <- egg::ggarrange(region_maps[[5]] + theme(plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
area_latitude_east_atl +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
legend.position = "none",
plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
nrow = 1, ncol = 2, top = T, bottom = T, widths = c(1.5,1))
range backtransformation not implemented in this coord; results may be wrong.
legend <- get_legend(area_latitude_east_atl)
figure_4_atlantic_merge <- plot_grid(figure_4_atlantic_merge_top, figure_4_atlantic_merge_bottom, ncol = 1, nrow = 2, labels = c("a.","b."), axis = "l", align = "v", hjust = -7, rel_heights = c(1.1,1))
figure_4_atlantic_merge_legend <- plot_grid(figure_4_atlantic_merge, legend, ncol = 2, nrow = 1, rel_widths = c(4,1))
ggsave(figure_4_atlantic_merge_legend, path = here::here("Figures", "Figure3_5"), filename = "Figure4_atlantic_latitude_area.jpg", height = 4, width = 6, unit = "in")

#west indian
(west_indian_merge_map_plot <- egg::ggarrange(region_maps[[4]],
area_latitude_west_ind
+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank() )
,
nrow = 1,
top = T))
range backtransformation not implemented in this coord; results may be wrong.



ggsave(plot = west_indian_merge_map_plot, filename = "west_indian_merge_map_plot_2degrees.jpg", width = 7, height = 3, units = "in")
#east indian
(east_indian_merge_map_plot <- egg::ggarrange(region_maps[[6]],
area_latitude_east_ind
+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank() )
,
nrow = 1,
top = T))
range backtransformation not implemented in this coord; results may be wrong.


ggsave(plot = east_indian_merge_map_plot, filename = "east_indian_merge_map_plot_2degrees.jpg", width = 7, height = 3, units = "in")
#both together
library(cowplot)
figure_5_indian_merge_top <- egg::ggarrange(region_maps[[4]] + theme(axis.title.x = element_blank(), plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
area_latitude_west_ind +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
axis.title.x = element_blank(),
legend.position = "none",
plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
nrow = 1, ncol = 2, top = T, bottom = T, widths = c(1.5,1))
range backtransformation not implemented in this coord; results may be wrong.

figure_5_indian_merge_bottom <- egg::ggarrange(region_maps[[6]] + theme(plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
area_latitude_east_ind +
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank(),
legend.position = "none",
plot.margin = unit(c(0.5,0.5,0.5,0.5),"cm")),
nrow = 1, ncol = 2, top = T, bottom = T, widths = c(1.5,1))
range backtransformation not implemented in this coord; results may be wrong.
legend <- get_legend(area_latitude_east_ind)
figure_5_indian_merge <- plot_grid(figure_5_indian_merge_top, figure_5_indian_merge_bottom, ncol = 1, nrow = 2, labels = c("a.","b."), axis = "l", align = "v", hjust = -7, rel_heights = c(1,1.2))
figure_5_indian_merge_legend <- plot_grid(figure_5_indian_merge, legend, ncol = 2, nrow = 1, rel_widths = c(4,1))
ggsave(figure_5_indian_merge_legend, path = here::here("Figures", "Figure3_5"), filename = "Figure5_indian_latitude_area.jpg", height = 4, width = 6, unit = "in")

And now final merge for figures 3-5
indian_latitude_2_plots <- ggarrange(west_indian_merge_map_plot, east_indian_merge_map_plot, nrow = 2, ncol = 1)
ggsave(indian_latitude_2_plots, file = "indian_latitude_2_plots.jpg", height = 6, unit = "in")
Saving 7 x 6 in image
ggsave(indian_latitude_2_plots, file = "indian_latitude_2_plots.pdf", height = 6, unit = "in")
pacific_latitude_2_plots <- ggarrange(west_pacific_merge_map_plot, east_pacific_merge_map_plot, nrow = 2, ncol = 1)
ggsave(pacific_latitude_2_plots, file = "pacific_latitude_2_plots.jpg", height = 6, unit = "in")
ggsave(pacific_latitude_2_plots, file = "pacific_latitude_2_plots.pdf", height = 6, unit = "in")
atlantic_latitude_2_plots <- ggarrange(west_atlantic_merge_map_plot, east_atlantic_merge_map_plot, nrow = 2, ncol = 1)
ggsave(atlantic_latitude_2_plots, file = "atlantic_latitude_2_plots.jpg", height = 6, unit = "in")
ggsave(atlantic_latitude_2_plots, file = "atlantic_latitude_2_plots.pdf", height = 6, unit = "in")
Summary Table of Shifts away from Equator
regional_statistics <- data.table()
regional_statistics[,region := rep(c("West Pacific", "East Pacific", "West Atlantic", "West Indian", "East Atlantic", "East Indian"),each = 2)][,hemisphere := rep(c("Northern", "Southern"),6)][,contractions := as.numeric(NA)][,expansions := as.numeric(NA)][,coef := as.numeric(NA)][,pvalue := as.numeric(NA)]
shelf_areas <- c("west_pac_shelf_areas", "east_pac_shelf_areas", "west_atl_shelf_areas", "west_ind_shelf_areas", "east_atl_shelf_areas", "east_ind_shelf_areas")
north_mods <- c("west_pac_north_mod", "east_pac_north_mod", "west_atl_north_mod", "west_ind_north_mod", "east_atl_north_mod", "east_ind_north_mod")
south_mods <- c("west_pac_south_mod", "east_pac_south_mod", "west_atl_south_mod", "west_ind_south_mod", "east_atl_south_mod", "east_ind_south_mod")
for (i in 1:length(shelf_areas)) {
this_shelf_area <- get(shelf_areas[i])
#populate west pacific
regional_statistics[region == unique(regional_statistics[,region])[i] & hemisphere == "Northern", "contractions"] <- round(this_shelf_area[latitude_end > 0 & change_above_2fold == -1,.N]/nrow(this_shelf_area[latitude_end > 0 & !is.na(percent_change)]),4)*100 #northern hemisphere contraction
regional_statistics[region == unique(regional_statistics[,region])[i] & hemisphere == "Southern", "contractions"] <- round(this_shelf_area[latitude_end < 0 & change_above_2fold == -1,.N]/nrow(this_shelf_area[latitude_end < 0 & !is.na(percent_change)]),4)*100 #southern hemisphere contraction
regional_statistics[region == unique(regional_statistics[,region])[i] & hemisphere == "Northern", "expansions"] <- round(this_shelf_area[latitude_end > 0 & change_above_2fold == 1,.N]/nrow(this_shelf_area[latitude_end > 0 & !is.na(percent_change)]),4)*100 #northern hemisphere expansion
regional_statistics[region == unique(regional_statistics[,region])[i] & hemisphere == "Southern", "expansions"] <- round(this_shelf_area[latitude_end < 0 & change_above_2fold == 1,.N]/nrow(this_shelf_area[latitude_end < 0 & !is.na(percent_change)]),4)*100 #southern hemisphere expansion
#northern mod
regional_statistics[region == unique(regional_statistics[,region])[i] & hemisphere == "Northern", "coef"] <- round(get(north_mods[i])$coefficients[2],2)
regional_statistics[region == unique(regional_statistics[,region])[i] & hemisphere == "Northern", "pvalue"] <- round(summary(get(north_mods[i]))$coefficients[,"Pr(>|t|)"][2],2)
#southern mod
regional_statistics[region == unique(regional_statistics[,region])[i] & hemisphere == "Southern", "coef"] <- round(get(south_mods[i])$coefficients[2],2)
regional_statistics[region == unique(regional_statistics[,region])[i] & hemisphere == "Southern", "pvalue"] <- round(summary(get(south_mods[i]))$coefficients[,"Pr(>|t|)"][2],2)
}
view(regional_statistics)
#save
fwrite(regional_statistics, file = "Table1_regional_stats.csv")
save(significant_changes.long, significant_changes, file = "significant_changes_2degrees.Rdata")
LS0tCnRpdGxlOiAiMsuaIFNoaWZ0cyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKClRoaXMgc2NyaXB0IGlzIHZlcnkgc2ltaWxhciB0byBkZWdyZWVfc2hpZnRzX3Byb2plY3RlZCwgYnV0IGxvb2tzIGF0IDLLmiBzaGlmdHMgaW5zdGVhZCBvZiAxy5pzaGlmdHMuIAoKCkRlZ3JlZSBzaGlmdHMKClNlY29uZCBwYXJ0IG9mIHBhcGVyLCBpZiB3ZSBtb3ZlIGF3YXkgZnJvbSBlcXVhdG9yIGJ5IDLLmiBkZWdyZWUsIGhvdyBtdWNoIGhhYml0YXQgZG8gd2UgZ2FpbiBvciBsb3N0CgpJIG5lZWQgdG8gbG9vayBhdCBzaGVsZiBhcmVhIGJ5IGRlZ3JlZXMgbGF0aXR1ZGUKCmBgYHtyIHNldHVwfQpsaWJyYXJ5KHJhc3RlcikKbGlicmFyeShzZikKbGlicmFyeShuY2RmNCkKbGlicmFyeShybWFwc2hhcGVyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkaXB0ZXN0KQpsaWJyYXJ5KG1vbWVudHMpCmxpYnJhcnkodmlyaWRpcykgI2NvbG9ycwpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoaHlkcm9UU00pICNoeXBzb21ldHJpYyBjdXJ2ZXMKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkobWFwdG9vbHMpCmxpYnJhcnkocmdkYWwpCmxpYnJhcnkocmdlb3MpCmxpYnJhcnkoU3BhREVTKQpsaWJyYXJ5KHJuYXR1cmFsZWFydGgpCmxpYnJhcnkocm5hdHVyYWxlYXJ0aGRhdGEpCmxpYnJhcnkoZ2duZXdzY2FsZSkKCgpldG9wb19zaGVsZl9kZiA8LSByZWFkUkRTKCJ+L0RvY3VtZW50cy9ncmFkIHNjaG9vbC9SdXRnZXJzL1JlcG9zaXRvcmllcy9zaGVsZl9oYWJpdGF0X2Rpc3RyaWJ1dGlvbi9ldG9wb19zaGVsZl9kZi5yZHMiKQojYnJpbmcgaW4gYmF0aHltZXRyeSBkYXRhIGZyYW1lIGZvciBzaGVsZiByZWdpb25zCgojTE1FcwpMTUVfc3BkZiA8LSByZWFkT0dSKCJMTUU2Ni9MTUVzNjYuc2hwIikgI3NwYXRpYWwgcG9pbnRzIGRhdGEgZnJhbWUgd2l0aCBhbGwgNjYgTE1FcwoKI3B1bGwgaW4gc2hhcGVmaWxlIGZvciBGQU8gc3RhdGlzdGljYWwgcmVnaW9uczogMy8xMS8yMDIxOiBodHRwOi8vd3d3LmZhby5vcmcvZ2VvbmV0d29yay9zcnYvZW4vbWFpbi5ob21lP3V1aWQ9YWMwMmE0NjAtZGE1Mi0xMWRjLTlkNzAtMDAxN2YyOTNiZDI4CkZBT19zcGRmIDwtIHJlYWRPR1IoIkZBT19BUkVBUy9GQU9fQVJFQVMuc2hwIikKCgoKCiNjb252ZXJ0IHRvIGVxdWFsIGFyZWEgcHJvamVjdGlvbgojZXF1YWxhcmVhcHJvamVjdGlvbjwtIGNycygiICtwcm9qPWVxZWFydGggIikKCiNUaGUgTGFtYmVydCBhemltdXRoYWwgZXF1YWwtYXJlYSBwcm9qZWN0aW9uIGlzIGEgcGFydGljdWxhciBtYXBwaW5nIGZyb20gYSBzcGhlcmUgdG8gYSBkaXNrLiBJdCBhY2N1cmF0ZWx5IHJlcHJlc2VudHMgYXJlYSBpbiBhbGwgcmVnaW9ucyBvZiB0aGUgc3BoZXJlLCBidXQgaXQgZG9lcyBub3QgYWNjdXJhdGVseSByZXByZXNlbnQgYW5nbGVzLgplcXVhbGFyZWFwcm9qZWN0aW9uPC0gY3JzKCIgK3Byb2o9bGFlYSAiKQoKCgoKYGBgCgpNYWtlIGJhdGh5bWV0cnkgZGF0YSBmcmFtZSBpbnRvIHJhc3RlciAodGhpcyB0YWtlcyBhIGJpdCkKCk5vdGUgdGhhdCBJIGFtIHRyeWluZyB0byB1c2UgdGhlIFtlcXVhbCBhcmVhIHByb2plY3Rpb25dKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLzIwMTgvMDkvcXVpY2staGl0LXVzaW5nLXRoZS1uZXctZXF1YWwtZWFydGgtcHJvamVjdGlvbi1pbi1yLykKYGBge3IgYmF0aHkgdG8gcmFzdGVyfQoKZXRvcG9fc2hlbGZfcmFzdGVyIDwtIHJhc3RlckZyb21YWVooZXRvcG9fc2hlbGZfZGYsIGNycyA9IGNycyhMTUVfc3BkZikpCgojcmVjbGFzc2lmeSBhbGwgdmFsdWVzIDwyMDAwbSBpbiBkZXB0aCB0byAxIGluc3RlYWQgb2YgYWN0dWFsIGRlcHRoCmV0b3BvX3NoZWxmX3Jhc3RlciA8LSByZWNsYXNzaWZ5KGV0b3BvX3NoZWxmX3Jhc3RlcixjYmluZCgtSW5mLCBJbmYsIDEpKQoKYGBgCgoKU2hvdWxkIGdvIGJ5IHByb2plY3Rpb25zIG9mIHdoZXJlIHNwZWNpZXMgYXJlIG1vdmluZzoKIk1hcmluZSBzcGVjaWVzICh+ODAlIGJlaW5nIGVjdG90aGVybXMgaW4gdGhlIGRhdGFiYXNlOyBFeHRlbmRlZCBEYXRhIEZpZy4gMikgaGF2ZSBtb3ZlZCB0b3dhcmRzIHRoZSBwb2xlcyBhdCBhIG1lYW4gKMKxcy5lLm0uKSBwYWNlIG9mIDUuOTIgwrEgMC45NCBrbSB5cuKIkjEgKG9uZS1zYW1wbGUgU3R1ZGVudOKAmXMgdC10ZXN0OiB0PTYuMjY7IGQuZi4gcmVzaWR1YWxzPTIzOyBQPTIuMjDDlzEw4oCTNiksIHdoaWNoIGlzIGFsbW9zdCBzaXggdGltZXMgZmFzdGVyIHRoYW4gdGVycmVzdHJpYWwgc3BlY2llcyAob25lLXdheSBhbmFseXNpcyBvZiB2YXJpYW5jZSAoQU5PVkEpOiBGPTEyLjY4OyBkLmYuIGZhY3Rvcj0xOyBkLmYuIHJlc2lkdWFscz00NTsgUD04Ljg4w5cxMOKAkzQpLiIgTGVub2lyIDIwMjAKCjUuOTIga20gKiAxMCA9IDU5LjIga20gaW4gMTAgeWVhcnMKCjU5LjIga20gaXMgaG93IG1hbnkgZGVncmVlcz8KCjHCsCA9IDExMSBrbSwgMsuaID0gMjIyLzU5LjI9IDMuNzUsIHNvIHJvdWdobHkgMzUgeWVhcnMKc28sIAoKYGBge3IgcXVpY2sgY29udmVyc2lvbn0KMjIyLzU5LjIKCmBgYAoKMC41MzMzy5ogaXMgcmVwcmVzZW50YXRpdmUgb2YgZGVjYWRhbCBzaGlmdHMsIGJ1dCwgZm9yIGJldHRlciB2aXN1YWxpemF0aW9uIGxldCdzIGdvIHdpdGggMsuaIChyZXByZXNlbnRhdGl2ZSBvZiA0MCB5ZWFyIHNoaWZ0cykKCkkgd2lsbCBwdXQgYXJlYXMgaW50byAyy5ogQmlucyAoMTgwIHRvdGFsIGRlZ3JlZXMsIHNvIDE4MC8yPTkwIHRvdGFsIGxhdGl0dWRpbmFsIGJpbnMpCgpgYGB7cn0KMTgwLzIKYGBgCgpIb3cgZG9lcyBjb250aW5lbnRhbCBzaGVsZiBoYWJpdGF0IGNoYW5nZSB3aXRoIGxhdGl0dWRlPwoKTG9vayBhdCBjb250aWd1b3VzIGNvYXN0IGxpbmVzLgoKSSBhbSBnb2luZyB0byBsZWF2ZSBvdXQgQW50YXJjdGljYSAoNjEpIGFuZCB0aGUgQXJjdGljICg2NCkgYXMgQW50YXJjdGljYSBkcm93bmVkIG91dCBwYXR0ZXJucyBpbiBsb3dlciBsYXRpdHVkZXMgYW5kIEFyY3RpYyBkb2Vzbid0IGhhdmUgbXVjaCBoYWJpdGF0IHNoYWxsb3dlciB0aGFuIDIwMDBtIHRvIGJlZ2luIHdpdGguIAoKTkI6IEZvciBzdWJhcmN0aWMgcmVnaW9ucywgSSdtIGdvaW5nIGJ5IFt0aGlzIGZpZ3VyZV0oaHR0cHM6Ly9jb21tb25zLndpa2ltZWRpYS5vcmcvd2lraS9GaWxlOklCQ0FPX2JldGFtYXAuanBnKSB3aGVyZSB0aGVyZSBhcHBlYXIgdG8gYmUgc3BsaXRzIGJldHdlZW4gRXVyb3BlIGFuZCBHcmVlbmxhbmQsIEdyZWVubGFuZCBhbmQgQ2FuYWRhLCBhbmQgQ2FuYWRhIGFuZCBBbGFza2EuIFRoaXMgaXMgZm9yIHN1cmUgdXAgZm9yIGRpc2N1c3Npb24sIGJ1dCBpdCBzZWVtcyBsaWtlIG9uY2Ugc3BlY2llcyBnZXQgYWJvdmUgdGhlIGNvbnRpbmVudHMsIGl0IGlzIGEgYmlnIG9mIGEgZnJlZSBmb3IgYWxsLiAKCkVhc3Rlcm4gQXRsYW50aWMgKDMpCi0xOSwgMjAsIDIxLCAyMiwgMjMsIDI0LCAyNSwgMjYsIDI3LCAyOCwgMjksIDU4LCA1OSwgNjAsIDYyCgogIDMvOCB1cGRhdGUgKG1ha2UgbW9yZSBMTUVzIHNlcnZlIG11bHRpcGxlIGNvYXN0bGluZXMsIGdldCByaWQgb2YgZGV0YWNoZWQgaXNsYW5kcyAgICAgKE5ldyBaZWFsYW5kLCBHcmVlbmxhbmQsIEljZWxhbmQpLCBhbmQgaW5uZXIgaXNsYW5kcyAoYWthIEJhbHRpYyBTZWEsIG1lZGl0ZXJycmFuaWVhbiAgICAgc2VhLCByZWQgc2VhLCBibGFjayBzZWEpKQoKICAtMjAsIDU4LCA1NywgNTYsIDIxLCA2MCwgMjIsIDIzKiBCYWx0aWMgU2VhLCAyNCwgMjUsIDI3LCAyOCwgMjksIDI2ICogTWVkaXRlcnJhbmVhbiwgNjIqIEJsYWNrIFNlYQoKV2VzdGVybiBBdGxhbnRpYyAoMikKLSA1LCA2LCA3LCA4LCA5LCAxMiwgMTQsIDE1LCAxNiwgMTcsIDE4LCA2MywgNjYKCiAgLTMvOCB1cGRhdGUsIExFQVZFIE9VVCBHUkVFTkxBTkQKCiAgLSA2NiwgNTUsIDYzKiogSHVkc29uIEJheSwgOSwgOCwgNywgNiwgNSoqIEdPTSwgMTIsIDE3LCAxNiwgMTUsIDE0ICoqd2hhdCBhYm91dCAgICBndWxmIG9mIG1leGljbyBhbmQgZ3VsZiBvZiBDYWxpZm9ybmlhPwoKRWFzdGVybiBQYWNpZmljICgxKQotIDEsIDIsIDMsIDQsIDExLCAxMywgNTQsIDU1ICoqQmVhdWZvcnQgc2VhIHRoaW5seSBsaW5rZWQgdG8gQ2h1a2NoaSBTZWEsIHNvIHNwbGl0IGhlcmUsIDY1CgogIC0zLzggdXBkYXRlCiAgLTU0LCAxLCAyLCA1MywgNjUsIDMsIDQqKiBHT0MsIDExLCAxMwoKV2VzdGVybiBJbmRpYW4gKDQpCi0zMCwgMzEsIDMyLCAzMwoKICAtMy84IHVwZGF0ZQogIC0zMCwgMzEsIDMzKipibGFjayBzZWEsIDMyCgpFYXN0ZXJuIEluZGlhbiAoNSkKLTM0LCAzOCwgNDMsIDQ0LCA0NQoKCiAgLTMvOCB1cGRhdGUKICAtanVzdCB1c2UgRkFPIHN0YXRpc3RpY2FsIGFyZWEgNTcKCldlc3Rlcm4gUGFjaWZpYyAoNikKMSwJMzUsCTM2LAkzNywJMzksCTQwLAk0MSwJNDIsCTQ2LAk0NywJNDgsCTQ5LAk1MCwJNTEsCTUyLAk1MywJNTQsCTU2LAk1NywJNjUKCiAgLTMvOCB1cGRhdGUKICAtNDIsIDQxLCA1MCwgNTEsIDUyLCA1MywgMSw1NCwgNTYsIDU3LCA1OCwgMjAsYW5kIEZBTyBzdGF0aXN0aWNhbCByZWdpb25zIDcxIGFuZCA2MQoKTWVyZ2UgTE1FcyBpbnRvIDYgY29hc3RsaW5lIHJlZ2lvbnMKCk1hc2tzIGZvciByZWdpb25zIHRoYXQgYXJlIG5vdCBpbmNsdWRlZCBpbiBMTUVzIChjb29yZGluYXRlcyB0YWtlbiBmcm9tIEdvb2dsZSBtYXBzKS4gV2lsbCBhZGQgaW4gYXJlYXMgdGhhdCBkbyBOT1Qgb3ZlcmxhcCB3aXRoIGV4aXN0aW5nIExNRXMgYXQgdGhlIGVuZC4gCgpgYGB7ciBtaXNzaW5nIGZyb20gTE1Fc30KI1dlc3Rlcm4gUGFjaWZpYywgaW5jbHVkZSBGQU8gc3RhdGlzdGljYWwgcmVnaW9ucyA3MSBhbmQgNjEKd2VzdF9wYWNfZmFvIDwtIEZBT19zcGRmW0ZBT19zcGRmJEZfQ09ERSAlaW4lIGMoNjEsNzEpLF0KCiNhZGp1c3QgZXh0ZW50IGEgYml0IHRvIGdldCByaWQgb2Ygcm9ndWUgLTE3OCBwb2ludHMKd2VzdF9wYWNfZmFvIDwtIGNyb3Aod2VzdF9wYWNfZmFvLCBleHRlbnQoOTQsIDE4MCwgLTI4LjE1LCA2Ni40MTQ4KSkKCndlc3RfcGFjX2Zhby5yYXN0ZXIgPC0gY3JvcChldG9wb19zaGVsZl9yYXN0ZXIsIGV4dGVudCg5NCwgMTgwLCAtMjguMTUsIDY2LjQxNDgpKQp3ZXN0X3BhY19mYW8ucmFzdGVyIDwtIG1hc2sod2VzdF9wYWNfZmFvLnJhc3Rlciwgd2VzdF9wYWNfZmFvKQoKI0Vhc3Rlcm4gSW5kaWFuIE9jZWFuCmVhc3RfaW5kX2ZhbyA8LSBGQU9fc3BkZltGQU9fc3BkZiRGX0NPREUgPT0gNTcsXQoKCmBgYAoKYGBge3IgbWVyZ2luZyBMTUVzfQp3ZXN0X3BhYyA8LSBjKDQyLCA0MSwgNTAsIDUxLCA1MiwgNTMsIDEsNTQsIDU2LCA1NywgNTgsIDIwKQplYXN0X3BhYyA8LSBjKDU0LCAxLCAyLCA1MywgNjUsIDMsIDQsIDExLCAxMykKd2VzdF9hdGwgPC0gYyg2NiwgNTUsIDYzLCA5LCA4LCA3LCA2LCA1LCAxMiwgMTcsIDE2LCAxNSwgMTQpCmVhc3RfYXRsIDwtIGMoMjAsIDU4LCA1NywgNTYsIDIxLCA2MCwgMjIsIDIzLCAyNCwgMjUsIDI3LCAyOCwgMjksIDI2LCA2MikKd2VzdF9pbmQgPC0gYygzMCwgMzEsIDMzLCAzMikKCiNzdWJyZWdpb25zIGJhc2VkIG9uIExNRV9udW1iZXIKd2VzdF9wYWNfc3BkZiA8LSBMTUVfc3BkZlsoTE1FX3NwZGYkTE1FX05VTUJFUikgJWluJSB3ZXN0X3BhYyxdCmVhc3RfcGFjX3NwZGYgPC0gTE1FX3NwZGZbKExNRV9zcGRmJExNRV9OVU1CRVIpICVpbiUgZWFzdF9wYWMsXQp3ZXN0X2F0bF9zcGRmIDwtIExNRV9zcGRmWyhMTUVfc3BkZiRMTUVfTlVNQkVSKSAlaW4lIHdlc3RfYXRsLF0Kd2VzdF9pbmRfc3BkZiA8LSBMTUVfc3BkZlsoTE1FX3NwZGYkTE1FX05VTUJFUikgJWluJSB3ZXN0X2luZCxdCmVhc3RfYXRsX3NwZGYgPC0gTE1FX3NwZGZbKExNRV9zcGRmJExNRV9OVU1CRVIpICVpbiUgZWFzdF9hdGwsXQplYXN0X2luZF9zcGRmIDwtIGVhc3RfaW5kX2ZhbwoKI2ZvciBzdWJyZWdpb25zIHRoYXQgc3BhbiAzNjAsIHdlIG5lZWQgdG8gY2hhbmdlIENSUyBhIGJpdApuZXdDUlNfd2VzdCA8LSAiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgK2xvbl93cmFwPTE4MCIgI3RoaXMgc2hpZnRzIDE4MCBkZWdyZWVzCndlc3RfcGFjX3NwZGZfc2hpZnQgPC0gc3BUcmFuc2Zvcm0od2VzdF9wYWNfc3BkZiwgQ1JTKG5ld0NSU193ZXN0KSkKd2VzdF9wYWNfc3BkZl9zaGlmdCA8LSBnQnVmZmVyKHdlc3RfcGFjX3NwZGZfc2hpZnQsIGJ5aWQ9VFJVRSwgd2lkdGg9MCkgI2dldHMgcmlkIG9mIGJ1ZmZlcnMsIGFsbG93cyBmb3IgdW5pb24KCm5ld0NSU19lYXN0IDwtICIrcHJvaj1sb25nbGF0ICtkYXR1bT1XR1M4NCArbG9uX3dyYXA9MTgwIiAjdGhpcyBzaGlmdHMgMTgwIGRlZ3JlZXMKZWFzdF9wYWNfc3BkZl9zaGlmdCA8LSBzcFRyYW5zZm9ybShlYXN0X3BhY19zcGRmLCBDUlMobmV3Q1JTX2Vhc3QpKQplYXN0X3BhY19zcGRmX3NoaWZ0IDwtIGdCdWZmZXIoZWFzdF9wYWNfc3BkZl9zaGlmdCwgYnlpZD1UUlVFLCB3aWR0aD0wKSAjZ2V0cyByaWQgb2YgYnVmZmVycywgYWxsb3dzIGZvciB1bmlvbgoKI3JvdGF0ZSByYXN0ZXIgZm9yIGJhdGh5bWV0cnkgKGd1aWRlZCBieSBleHRlbnQgb2YgZXRvcG9zaGVsZiByYXN0ZXIsICB0aGF0J3Mgd2h5IHdhY2t5ICNzKQp4MSA8LSBjcm9wKGV0b3BvX3NoZWxmX3Jhc3RlciwgZXh0ZW50KC0xODAuMDE2NywgLTAuMDE2NywgLTkwLjAxNjY3LCA5MC4wMTY2NykpCngyIDwtIGNyb3AoZXRvcG9fc2hlbGZfcmFzdGVyLCBleHRlbnQoMCwgMTgwLjAxNjcsIC05MC4wMTY2NywgOTAuMDE2NjcpKSAgIApleHRlbnQoeDEpIDwtIGMoMTgwLjAxNjcsIDM2MC4wMTY3LCAtOTAuMDE2NjcgLCA5MC4wMTY2NykKZXRvcG9fc2hlbGZfcmFzdGVyXzE4MCA8LSBtZXJnZSh4MSwgeDIpCgojZ2V0IHJpZCBvZiBidWZmZXIgZm9yIGVhc3QgYXRsIGFzIHdlbGwgdG8gYWxsb3cgZm9yIHVuaW9uCgplYXN0X2F0bF9zcGRmX25vYnVmIDwtIGdCdWZmZXIoZWFzdF9hdGxfc3BkZiwgYnlpZD1UUlVFLCB3aWR0aD0wKQoKcmVnaW9uX25hbWVzIDwtIGMoIndlc3RfcGFjX3NwZGZfc2hpZnQiLCAiZWFzdF9wYWNfc3BkZl9zaGlmdCIsICJ3ZXN0X2F0bF9zcGRmIiwgIndlc3RfaW5kX3NwZGYiLCAiZWFzdF9hdGxfc3BkZl9ub2J1ZiIsICJlYXN0X2luZF9zcGRmIikKCgoKI2Rpc3NvbHZlIGFsbCBwb2x5Z29ucyBieSByZWdpb24KZm9yIChpIGluIDE6bGVuZ3RoKHJlZ2lvbl9uYW1lcykpIHsKICBuYW1lIDwtIHBhc3RlMChyZWdpb25fbmFtZXNbaV0sICJfYWdnIikKICBhc3NpZ24obmFtZSwgZ1VuYXJ5VW5pb24oZ2V0KHJlZ2lvbl9uYW1lc1tpXSkpKSAjZGlzc29sdmUgcG9seWdvbnMgd2l0aGluIGNvYXN0bGluZSByZWdpb24gaW50byBvbmUKfQoKYGBgCgoKRXh0cmFjdCBiYXRoeW1ldHJ5IGRhdGEgZnJvbSBwb2x5Z29uIG9ubHkgdG8gbWFrZSBzdXJlIHdlJ3JlIGxpbWl0aW5nIHRvIHNoZWxmIHJlZ2lvbnMgYWJvdmUgMjAwMCBtZXRlcnMKCmBgYHtyIHBvbHlnb24gdG8gcmFzdGVyfQoKcmVnaW9uX25hbWVzX3NoaWZ0IDwtIHJlZ2lvbl9uYW1lc1sxOjJdCgoKcmVnaW9uX25hbWVzX25vc2hpZnQgPC0gcmVnaW9uX25hbWVzWzM6Nl0KCgpmb3IgKGkgaW4gMTpsZW5ndGgocmVnaW9uX25hbWVzX25vc2hpZnQpKSB7CiNjcm9wIGJhdGh5bWV0cnkgbGF5ZXIgdG8gTE1FIHN1YnNldCAoY29udGluZW50YWwgc2hlbGYgaGFiaXRhdCBpbiBMTUVzKQogIHJhc3Rlcl9leHRlbnQgPC0KICAgICAgIGNyb3AoZXRvcG9fc2hlbGZfcmFzdGVyLCBleHRlbnQoZ2V0KHBhc3RlMChyZWdpb25fbmFtZXNfbm9zaGlmdFtpXSwgIl9hZ2ciKSkpKQoKI3doaWNoIGFyZWFzIG9mIHJhc3RlciBmYWxsIHdpdGhpbiBib3JkZXJzPwogIGFzc2lnbihwYXN0ZTAocmVnaW9uX25hbWVzX25vc2hpZnRbaV0sICJfbWFzayIpLAogICAgICAgbWFzayhyYXN0ZXJfZXh0ZW50LCBnZXQocGFzdGUwKHJlZ2lvbl9uYW1lc19ub3NoaWZ0W2ldLCAiX2FnZyIpKSkpCiAgCiAgICBhc3NpZ24ocGFzdGUwKHJlZ2lvbl9uYW1lc19ub3NoaWZ0W2ldLCAiX21hc2tfMXMiKSwKICAgICAgIHJlY2xhc3NpZnkoZ2V0KHBhc3RlMChyZWdpb25fbmFtZXNfbm9zaGlmdFtpXSwgIl9tYXNrIikpLCBjYmluZCgtSW5mLCBJbmYsIDEpKSkKCiAgCgp9CgojZWRpdCBmb3IgZWFzdF9wYWNfc3BkZl9zaGlmdCBhbmQgd2VzdF9wYWNfc3BkZl9zaGlmdCAoKzE4MMuaKQoKZm9yIChpIGluIDE6bGVuZ3RoKHJlZ2lvbl9uYW1lc19zaGlmdFsxOjJdKSkgewojY3JvcCBiYXRoeSBsYXllciB0byBMTUUgc3Vic2V0CiAgcmFzdGVyX2V4dGVudCA8LQogICAgICAgY3JvcChldG9wb19zaGVsZl9yYXN0ZXJfMTgwLCBleHRlbnQoZ2V0KHBhc3RlMChyZWdpb25fbmFtZXNfc2hpZnRbaV0sICJfYWdnIikpKSkKCiN3aGljaCBhcmVhcyBvZiByYXN0ZXIgZmFsbCB3aXRoaW4gYm9yZGVycz8KICBhc3NpZ24ocGFzdGUwKHJlZ2lvbl9uYW1lc19zaGlmdFtpXSwgIl9tYXNrIiksCiAgICAgICBtYXNrKHJhc3Rlcl9leHRlbnQsIGdldChwYXN0ZTAocmVnaW9uX25hbWVzX3NoaWZ0W2ldLCAiX2FnZyIpKSkpCiAgCiAgICBhc3NpZ24ocGFzdGUwKHJlZ2lvbl9uYW1lc19zaGlmdFtpXSwgIl9tYXNrXzFzIiksCiAgICAgICByZWNsYXNzaWZ5KGdldChwYXN0ZTAocmVnaW9uX25hbWVzX3NoaWZ0W2ldLCAiX21hc2siKSksIGNiaW5kKC1JbmYsIEluZiwgMSkpKQoKfQoKYGBgCgpNZXJnZSBpbiBhcmVhcyB1bmFjY291bnRlZCBmb3IgYnkgTE1FcyAoUGhpbGlwcGluZXMsIFN1bWF0cmEsIFBhcHVhIE5ldyBHdWluZWEpCgpgYGB7ciBtZXJnZSBub24tTE1FIHJlZ2lvbnN9CiN3ZXN0ZXJuIHBhY2lmaWMgb2NlYW4Kd2VzdF9wYWNfc3BkZl9tYXNrX2Z1bGwgPC0gbWVyZ2Uod2VzdF9wYWNfZmFvLnJhc3Rlciwgd2VzdF9wYWNfc3BkZl9zaGlmdF9tYXNrXzFzKQpgYGAKCgpIb3cgdG8gY2FsY3VsYXRlIGFyZWE/CgotIHJhc3Rlcjo6YXJlYSgpIFRISVMgSVMgV0hBVCBXRSdSRSBHT0lORyBXSVRIClJhc3RlciBvYmplY3RzOiBDb21wdXRlIHRoZSBhcHByb3hpbWF0ZSBzdXJmYWNlIGFyZWEgb2YgY2VsbHMgaW4gYW4gdW5wcm9qZWN0ZWQgKGxvbmdpdHVkZS9sYXRpdHVkZSkgUmFzdGVyIG9iamVjdC4gSXQgaXMgYW4gYXBwcm94aW1hdGlvbiBiZWNhdXNlIGFyZWEgaXMgY29tcHV0ZWQgYXMgdGhlIGhlaWdodCAobGF0aXR1ZGluYWwgc3Bhbikgb2YgYSBjZWxsICh3aGljaCBpcyBjb25zdGFudCBhbW9uZyBhbGwgY2VsbHMpIHRpbWVzIHRoZSB3aWR0aCAobG9uZ2l0dWRpbmFsIHNwYW4pIGluIHRoZSAobGF0aXR1ZGluYWwpIG1pZGRsZSBvZiBhIGNlbGwuIFRoZSB3aWR0aCBpcyBzbWFsbGVyIGF0IHRoZSBwb2xld2FyZCBzaWRlIHRoYW4gYXQgdGhlIGVxdWF0b3Itd2FyZCBzaWRlIG9mIGEgY2VsbC4gVGhpcyB2YXJpYXRpb24gaXMgZ3JlYXRlc3QgbmVhciB0aGUgcG9sZXMgYW5kIHRoZSB2YWx1ZXMgYXJlIHRodXMgbm90IHZlcnkgcHJlY2lzZSBmb3IgdmVyeSBoaWdoIGxhdGl0dWRlcy4gSWYgeCBpcyBhIFJhc3Rlciogb2JqZWN0OiBSYXN0ZXJMYXllciBvciBSYXN0ZXJCcmljay4gQ2VsbCB2YWx1ZXMgcmVwcmVzZW50IHRoZSBzaXplIG9mIHRoZSBjZWxsIGluIGttMiwgb3IgdGhlIHJlbGF0aXZlIHNpemUgaWYgd2VpZ2h0cz1UUlVFCgoKCi0gcmFzdGVyOjphcmVhKCkgClNwYXRpYWxQb2x5Z29uczogQ29tcHV0ZSB0aGUgYXJlYSBvZiB0aGUgc3BhdGlhbCBmZWF0dXJlcy4gV29ya3MgZm9yIGJvdGggcGxhbmFyIGFuZCBhbmd1bGFyIChsb24vbGF0KSBjb29yZGluYXRlIHJlZmVyZW5jZSBzeXN0ZW1zLiBJZiB4IGlzIGEgU3BhdGlhbFBvbHlnb25zKiBvYmplY3Q6IGFyZWEgb2YgZWFjaCBzcGF0aWFsIG9iamVjdCBpbiBzcXVhcmVkIG1ldGVycyBpZiB0aGUgQ1JTIGlzIGxvbmdpdHVkZS9sYXRpdHVkZSwgb3IgaW4gc3F1YXJlZCBtYXAgdW5pdHMgKHR5cGljYWxseSBtZXRlcikKCi0gcmdlb3M6OmdBcmVhClJldHVybnMgdGhlIGFyZWEgb2YgdGhlIGdlb21ldHJ5IGluIHRoZSB1bml0cyBvZiB0aGUgY3VycmVudCBwcm9qZWN0aW9uLiBCeSBkZWZpbml0aW9uIG5vbi1bTVVMVEldUE9MWUdPTiBnZW9tZXRyaWVzIGhhdmUgYW4gYXJlYSBvZiAwLiBUaGUgYXJlYSBvZiBhIFBPTFlHT04gaXMgdGhlIGFyZWEgb2YgaXRzIHNoZWxsIGxlc3MgdGhlIGFyZWEgb2YgYW55IGhvbGVzLiBOb3RlIHRoYXQgdGhpcyB2YWx1ZSBtYXkgYmUgZGlmZmVyZW50IGZyb20gdGhlIGFyZWEgc2xvdCBvZiB0aGUgUG9seWdvbnMgY2xhc3MgYXMgdGhpcyB2YWx1ZSBkb2VzIG5vdCBzdWJ0cmFjdCB0aGUgYXJlYSBvZiBhbnkgaG9sZXMgaW4gdGhlIGdlb21ldHJ5LgoKCk5vdywgd2Ugd2lsbCBzcGxpdCBlYWNoIGNvYXN0bGluZSByYXN0ZXIgaW50byBsYXRpdHVkaW5hbCBiaW5zIG9mIDLLmgoKSWYgdGhlc2UgaGF2ZSBhbHJlYWR5IGJlZW4gZG9uZSwgbG9hZCBpbiBmcm9tIGZpbGUgaW5zdGVhZCBvZiByZWNyZWF0aW5nIGJlY2F1c2UgdGhhdCB0YWtlcyBzb21lIHRpbWUuCmBgYHtyIGxvYWQgaW4gZnJvbSBmaWxlc30KbG9hZCgid2VzdF9wYWNfc2hlbGZfYXJlYXNfMmRlZ3JlZXMuUmRhdGEiKQpsb2FkKCJ3ZXN0X2F0bF9zaGVsZl9hcmVhc18yZGVncmVlcy5SZGF0YSIpCmxvYWQoIndlc3RfaW5kX3NoZWxmX2FyZWFzXzJkZWdyZWVzLlJkYXRhIikKbG9hZCgiZWFzdF9wYWNfc2hlbGZfYXJlYXNfMmRlZ3JlZXMuUmRhdGEiKQpsb2FkKCJlYXN0X2F0bF9zaGVsZl9hcmVhc18yZGVncmVlcy5SZGF0YSIpCmxvYWQoImVhc3RfaW5kX3NoZWxmX2FyZWFzXzJkZWdyZWVzLlJkYXRhIikKYGBgCgoKV2VzdGVybiBQYWNpZmljCmBgYHtyIHNwbGl0IHJhc3RlciBmb3Igd2VzdGVybiBwYWNpZmljfQpub3J0aF9leHRlbnQgPC0gYyh4bWluKHdlc3RfcGFjX3NwZGZfbWFza19mdWxsKSwgeG1heCh3ZXN0X3BhY19zcGRmX21hc2tfZnVsbCksIDAsIHltYXgod2VzdF9wYWNfc3BkZl9tYXNrX2Z1bGwpKQpzb3V0aF9leHRlbnQgPC0gYyh4bWluKHdlc3RfcGFjX3NwZGZfbWFza19mdWxsKSwgeG1heCh3ZXN0X3BhY19zcGRmX21hc2tfZnVsbCksIHltaW4od2VzdF9wYWNfc3BkZl9tYXNrX2Z1bGwpLCAwKQoKI2Nyb3Agd2VzdF9wYWMgcmFzdGVyIGFib3ZlIGFuZCBiZWxvdyAwCndlc3RfcGFjX3NwZGZfc2hpZnRfYWdnX25vcnRoIDwtIGNyb3Aod2VzdF9wYWNfc3BkZl9tYXNrX2Z1bGwsIGV4dGVudChub3J0aF9leHRlbnQpKQoKd2VzdF9wYWNfc3BkZl9zaGlmdF9hZ2dfc291dGggPC0gY3JvcCh3ZXN0X3BhY19zcGRmX21hc2tfZnVsbCwgZXh0ZW50KHNvdXRoX2V4dGVudCkpCgojdW5mb3J0dW5hdGVseSwgSSB0aGluayBJIG1heSBoYXZlIHRvIGp1c3QgZG8gdGhpcyBtYW51YWxseSAodWdseSwgSSBrbm93KQoKI2FsbCBjaHVua3MgZm9yIHdlc3QgcGFjaWZpYwp3ZXN0X3BhY19ub3J0aF9sYXRpdHVkZXMgPC0gc2VxKDAsIHltYXgod2VzdF9wYWNfc3BkZl9tYXNrX2Z1bGwpLCBieSA9IDIpCndlc3RfcGFjX3NvdXRoX2xhdGl0dWRlcyA8LSBzZXEoMCwgeW1pbih3ZXN0X3BhY19zcGRmX21hc2tfZnVsbCksIGJ5ID0gLTIpCgojc2V0dXAgZGF0YSB0YWJsZSB0byBwb3B1bGF0ZSBpbiBsb29wLCBzdWJ0cmFjdGluZyBvbmUgdG8gYWxsb3cgZm9yIGJpbnMKd2VzdF9wYWNfc2hlbGZfYXJlYXMgPC0gYXMuZGF0YS50YWJsZShtYXRyaXgobnJvdyA9IChsZW5ndGgod2VzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xK2xlbmd0aCh3ZXN0X3BhY19zb3V0aF9sYXRpdHVkZXMpLTEpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKd2VzdF9wYWNfc2hlbGZfYXJlYXNbLCBsYXRpdHVkZV9zdGFydCA6PSBhcy5udW1lcmljKFYxKV1bLCBsYXRpdHVkZV9lbmQgOj0gYXMubnVtZXJpYyhWMSldWywgYXJlYV9yYXN0ZXJhcmVhIDo9IGFzLm51bWVyaWMoVjEpXVssIGFyZWFfZXF1YWxhcmVhcHJvaiA6PSBhcy5udW1lcmljKFYxKV1bLCBhcmVhX3JnZW9zX2dBcmVhIDo9IGFzLm51bWVyaWMoVjEpXVssIFYxIDo9IE5VTExdCgojbG9vcCBmb3Igbm9ydGgKZm9yIChpIGluIDE6KGxlbmd0aCh3ZXN0X3BhY19ub3J0aF9sYXRpdHVkZXMpLTEpKSB7CiAgI3NldHRpbmcgdXAgZXh0ZW50IGZvciBzbGljaW5nIGJ5IG1pbiBhbmQgbWF4IGxvbmdpdHVkZXMsIGFuZCBpIHRvIGkrMSBsYXRpdHVkZXMKICBub3J0aF9leHRlbnQgPC0gYyh4bWluKHdlc3RfcGFjX3NwZGZfbWFza19mdWxsKSwgeG1heCh3ZXN0X3BhY19zcGRmX21hc2tfZnVsbCksIHdlc3RfcGFjX25vcnRoX2xhdGl0dWRlc1tpXSwgd2VzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzW2krMV0pCiAgCiAgI2Nyb3AgcmFzdGVyIHNlZ2VtZW50IGJhc2VkIG9uIGJpbiBleHRlbnQKICBzZWdtZW50X25vcnRoIDwtIGNyb3Aod2VzdF9wYWNfc3BkZl9tYXNrX2Z1bGwsIGV4dGVudChub3J0aF9leHRlbnQpKQogIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggbGF0aXR1ZGluYWwgYmluCiAgd2VzdF9wYWNfc2hlbGZfYXJlYXNbaSwgImxhdGl0dWRlX3N0YXJ0Il0gPC0gd2VzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzW2ldCiAgd2VzdF9wYWNfc2hlbGZfYXJlYXNbaSwgImxhdGl0dWRlX2VuZCJdIDwtIHdlc3RfcGFjX25vcnRoX2xhdGl0dWRlc1tpKzFdCiAgCiAgaWYoYWxsKGlzLm5hKHZhbHVlcyhzZWdtZW50X25vcnRoKSkpKSB7ICNpZiB0aGVyZSdzIG5vIHNoZWxmIGFyZWEgd2l0aGluIGEgYmluLCBhbGwgYXJlYSA9IDAKICAgIAogIHdlc3RfcGFjX3NoZWxmX2FyZWFzW2ksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSAwCiAgd2VzdF9wYWNfc2hlbGZfYXJlYXNbaSwgImFyZWFfcmFzdGVyYXJlYSJdIDwtIDAKICB3ZXN0X3BhY19zaGVsZl9hcmVhc1tpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtIDAKCiAgCiAgcHJpbnQoaSkKICAgIAogIH0gZWxzZSB7ICNpZiB0aGVyZSBpcyBzaGVsZiBhcmVhIHdpdGhpbiB0aGUgYmluLCBjYWxjdWxhdGUgYXJlYSBvZiBzbGljZQogIAogICAgI3Jhc3RlciBhcmVhIGNhbGN1bGF0aW9uCiAgICAgICNnZXQgc2l6ZXMgb2YgYWxsIGNlbGxzIGluIHJhc3RlciBba20yXQogICAgY2VsbF9zaXplX3Jhc3RlcjwtYXJlYShzZWdtZW50X25vcnRoLCBuYS5ybT1UUlVFLCB3ZWlnaHRzPUZBTFNFKQogICAgCiAgICAjZGVsZXRlIE5BcyBmcm9tIHZlY3RvciBvZiBhbGwgcmFzdGVyIGNlbGxzCiAgICBjZWxsX3NpemVfcmFzdGVyPC1jZWxsX3NpemVfcmFzdGVyWyFpcy5uYShzZWdtZW50X25vcnRoKV0KICAgIAogICAgI2NvbXB1dGUgYXJlYSBvZiBhbGwgY2VsbHMgaW4gZ2VvX3Jhc3RlcgogICAgI2Z1bGwgYXJlYSAgICAgICAgICA8LSB0b3RhbCAjIGdyaWQgY2VsbHMgICAgICogbWVkaWFuIGNlbGwgYXJlYSAodXNpbmcgbWVkaWFuIGNhcmVzIGxlc3MgYWJvdXQgZXh0cmVtZSB2YWx1ZXMpCiAgICBzZWdtZW50X2FyZWFfcmFzdGVyIDwtIGxlbmd0aChjZWxsX3NpemVfcmFzdGVyKSptZWRpYW4oY2VsbF9zaXplX3Jhc3RlcikgI2luIGttXjIKICAgIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcmFzdGVyIGFyZWEKICB3ZXN0X3BhY19zaGVsZl9hcmVhc1tpLCAiYXJlYV9yYXN0ZXJhcmVhIl0gPC0gc2VnbWVudF9hcmVhX3Jhc3RlcgogICAgCiAgI2NvbnZlcnQgdG8gc3BhdGlhbCBwb2x5Z29ucyB0byBjaGVjayBhcmVhIGNhbGN1bGF0aW9ucwogICAgCiAgI2NvbnZlcnQgc2VnbWVudCBmcm9tIHJhc3RlciB0byBwb2x5Z29uLCBlYWNoIGNlbGwgZnJvbSB0aGUgcmFzdGVyIGlzIGFuIGluZGVwZW5kZW50IHBvbHlnb24sIChkaXNzb2x2ZSBtZWFucyBhbGwgY2VsbHMgd2l0aCBhIHZhbHVlIG9mIDEgYXJlIGEgc2luZ2xlIHBvbHlnb24gaWYgY29ubmVjdGVkKQogIHNlZ21lbnRfbm9ydGguc3AgPC0gcmFzdGVyVG9Qb2x5Z29ucyhzZWdtZW50X25vcnRoLCBkaXNzb2x2ZSA9IFQpCiAgCiAgIyAgSWYgeCBpcyBhIFNwYXRpYWxQb2x5Z29ucyogb2JqZWN0OiBhcmVhIG9mIGVhY2ggc3BhdGlhbCBvYmplY3QgaW4gc3F1YXJlZCBtZXRlcnMgaWYgdGhlIENSUyBpcyBsb25naXR1ZGUvbGF0aXR1ZGUsIG9yIGluIHNxdWFyZWQgbWFwIHVuaXRzICh0eXBpY2FsbHkgbWV0ZXIpCiAgCiAgICAjcHJvamVjdCB0byBlcXVhbCBlYXJ0aCBhcmVhIHByb2plY3Rpb24KICBzZWdtZW50X25vcnRoLnNwLkVBIDwtIHNwVHJhbnNmb3JtKHNlZ21lbnRfbm9ydGguc3AsIENSU29iaiA9IGVxdWFsYXJlYXByb2plY3Rpb24pCgogICNjYWxjdWxhdGUgYXJlYSBvZiB0aGUgc3BhdGlhbCBvYmplY3QgaW4gbV4yCiAgICBwb2x5Z29uX3NpemUuc3AgPC0gYXJlYShzZWdtZW50X25vcnRoLnNwLkVBKQoKICAgICNjb252ZXJ0IGZyb20gbV4yIHRvIGttXjIKICAgIHNlZ21lbnRfYXJlYV9lcXVhbGFyZWEgPC0gcG9seWdvbl9zaXplLnNwLzFlNgoKICAgICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcG9seWdvbiBhcmVhIHVzaW5nIHJhc3RlciBjYWxjdWxhdGlvbgogIHdlc3RfcGFjX3NoZWxmX2FyZWFzW2ksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSBzZWdtZW50X2FyZWFfZXF1YWxhcmVhCiAgCiAgI2FuZCB0aGVuIHBsYWluIGFuZCBzaW1wbGUgYWxzbyB1c2luZyByZ2Vvczo6Z0FyZWEKICAKICBhcmVhX3JnZW9zX2dBcmVhIDwtIGdBcmVhKHNlZ21lbnRfbm9ydGguc3AuRUEpLzFlNgogIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcG9seWdvbiBhcmVhIHVzaW5nIHJlZ2VvcyBjYWxjdWxhdGlvbgogIHdlc3RfcGFjX3NoZWxmX2FyZWFzW2ksICJhcmVhX3JnZW9zX2dBcmVhIl0gPC0gICBhcmVhX3JnZW9zX2dBcmVhCiAgCiAgcHJpbnQoaSkKICB9IAp9CgojbG9vcCBmb3Igc291dGgKZm9yIChpIGluIDE6KGxlbmd0aCh3ZXN0X3BhY19zb3V0aF9sYXRpdHVkZXMpLTEpKSB7CiAgc291dGhfZXh0ZW50IDwtIGMoeG1pbih3ZXN0X3BhY19zcGRmX21hc2tfZnVsbCksIHhtYXgod2VzdF9wYWNfc3BkZl9tYXNrX2Z1bGwpLCB3ZXN0X3BhY19zb3V0aF9sYXRpdHVkZXNbaSsxXSwgd2VzdF9wYWNfc291dGhfbGF0aXR1ZGVzW2ldKSAjb3JkZXI9IHhtaW4sIHhtYXgsIHltaW4sIHltYXgpCiAgCiAgI3Jhc3RlciBzZWdtZW50CiAgc2VnbWVudF9zb3V0aCA8LSBjcm9wKHdlc3RfcGFjX3NwZGZfbWFza19mdWxsLCBleHRlbnQoc291dGhfZXh0ZW50KSkKICAKICAjYWRkIGxhdGl0dWRlIGJpbiBpbmZvIHRvIGRhdGEgdGFibGUKICAgIHdlc3RfcGFjX3NoZWxmX2FyZWFzW2krKGxlbmd0aCh3ZXN0X3BhY19ub3J0aF9sYXRpdHVkZXMpLTEpLCAibGF0aXR1ZGVfc3RhcnQiXSA8LSB3ZXN0X3BhY19zb3V0aF9sYXRpdHVkZXNbaV0KICB3ZXN0X3BhY19zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImxhdGl0dWRlX2VuZCJdIDwtIHdlc3RfcGFjX3NvdXRoX2xhdGl0dWRlc1tpKzFdCiAgCiAgCiAgaWYoYWxsKGlzLm5hKHZhbHVlcyhzZWdtZW50X3NvdXRoKSkpKSB7ICNpZiB0aGVyZSdzIG5vIHNoZWxmIGFyZWEgd2l0aGluIGEgYmluLCBtZWFuaW5nIHRoZXJlJ3Mgbm8gc2hlbGYgYXJlYSBhdCB0aGF0IGxhdGl0dWRlCiAgICAKICB3ZXN0X3BhY19zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfZXF1YWxhcmVhcHJvaiJdIDwtIDAKICB3ZXN0X3BhY19zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfcmFzdGVyYXJlYSJdIDwtIDAKICAgIHdlc3RfcGFjX3NoZWxmX2FyZWFzW2krKGxlbmd0aCh3ZXN0X3BhY19ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtIDAKICAKICBwcmludChpKQogICAgCiAgfSBlbHNlIHsKICAKICAgICNyYXN0ZXIgYXJlYSBjYWxjdWxhdGlvbgogICAgICAjZ2V0IHNpemVzIG9mIGFsbCBjZWxscyBpbiByYXN0ZXIgW2ttMl0KICAgIGNlbGxfc2l6ZV9yYXN0ZXI8LWFyZWEoc2VnbWVudF9zb3V0aCwgbmEucm09VFJVRSwgd2VpZ2h0cz1GQUxTRSkKICAgIAogICAgI2RlbGV0ZSBOQXMgZnJvbSB2ZWN0b3Igb2YgYWxsIHJhc3RlciBjZWxscwogICAgY2VsbF9zaXplX3Jhc3RlcjwtY2VsbF9zaXplX3Jhc3RlclshaXMubmEoc2VnbWVudF9zb3V0aCldCiAgICAjY29tcHV0ZSBhcmVhIG9mIGFsbCBjZWxscyBpbiBnZW9fcmFzdGVyCiAgICAjZnVsbCBhcmVhICAgICAgICAgIDwtIHRvdGFsICMgZ3JpZCBjZWxscyAgICAgKiBtZWRpYW4gY2VsbCBhcmVhICh1c2luZyBtZWRpYW4gY2FyZXMgbGVzcyBhYm91dCBleHRyZW1lIHZhbHVlcykKICAgIHNlZ21lbnRfYXJlYV9yYXN0ZXIgPC0gbGVuZ3RoKGNlbGxfc2l6ZV9yYXN0ZXIpKm1lZGlhbihjZWxsX3NpemVfcmFzdGVyKQogICAgCiAgI3BvcHVsYXRlIGRhdGEgdGFibGUgd2l0aCByYXN0ZXIgYXJlYQogIHdlc3RfcGFjX3NoZWxmX2FyZWFzW2krKGxlbmd0aCh3ZXN0X3BhY19ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yYXN0ZXJhcmVhIl0gPC0gc2VnbWVudF9hcmVhX3Jhc3RlcgogICAgCiAgCiAgI2NvbnZlcnQgdG8gc3BhdGlhbCBwb2x5Z29ucyB0byBjaGVjayBhcmVhIGNhbGN1bGF0aW9ucwogICAgCiAgI2NvbnZlcnQgc2VnbWVudCBmcm9tIHJhc3RlciB0byBwb2x5Z29uLCBlYWNoIGNlbGwgZnJvbSB0aGUgcmFzdGVyIGlzIGFuIGluZGVwZW5kZW50IHBvbHlnb24sIChkaXNzb2x2ZSBtZWFucyBhbGwgY2VsbHMgd2l0aCBhIHZhbHVlIG9mIDEgYXJlIGEgc2luZ2xlIHBvbHlnb24gaWYgY29ubmVjdGVkKQogIHNlZ21lbnRfc291dGguc3AgPC0gcmFzdGVyVG9Qb2x5Z29ucyhzZWdtZW50X3NvdXRoLCBkaXNzb2x2ZSA9IFQpCiAgCiAgIyAgSWYgeCBpcyBhIFNwYXRpYWxQb2x5Z29ucyogb2JqZWN0OiBhcmVhIG9mIGVhY2ggc3BhdGlhbCBvYmplY3QgaW4gc3F1YXJlZCBtZXRlcnMgaWYgdGhlIENSUyBpcyBsb25naXR1ZGUvbGF0aXR1ZGUsIG9yIGluIHNxdWFyZWQgbWFwIHVuaXRzICh0eXBpY2FsbHkgbWV0ZXIpCiAgCiAgICAjcHJvamVjdCB0byBlcXVhbCBlYXJ0aCBhcmVhIHByb2plY3Rpb24KICBzZWdtZW50X3NvdXRoLnNwLkVBIDwtIHNwVHJhbnNmb3JtKHNlZ21lbnRfc291dGguc3AsIENSU29iaiA9IGVxdWFsYXJlYXByb2plY3Rpb24pCgogICNjYWxjdWxhdGUgYXJlYSBvZiB0aGUgc3BhdGlhbCBvYmplY3QgaW4gbV4yCiAgICBwb2x5Z29uX3NpemUuc3AgPC0gYXJlYShzZWdtZW50X3NvdXRoLnNwLkVBKQoKICAgICNjb252ZXJ0IGZyb20gbV4yIHRvIGttXjIKICAgIHNlZ21lbnRfYXJlYV9lcXVhbGFyZWEgPC0gcG9seWdvbl9zaXplLnNwLzFlNgoKICAgICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggYXJlYSBvZiBwb2x5Z29uIGZyb20gcmFzdGVyOjphcmVhIGZ1bmN0aW9uCiAgd2VzdF9wYWNfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKHdlc3RfcGFjX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSBzZWdtZW50X2FyZWFfZXF1YWxhcmVhCiAgCiAgI2FuZCB0aGVuIHBsYWluIGFuZCBzaW1wbGUgYWxzbyB1c2luZyByZ2Vvczo6Z0FyZWEKICAKICBhcmVhX3JnZW9zX2dBcmVhIDwtIGdBcmVhKHNlZ21lbnRfc291dGguc3AuRUEpLzFlNgogIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIGZyb20gcmdlb3MgYXJlYSBjYWxjdWxhdGlvbiBmb3IgcHJvamVjdGVkIHBvbHlnb24KICB3ZXN0X3BhY19zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfcmdlb3NfZ0FyZWEiXSA8LSAgIGFyZWFfcmdlb3NfZ0FyZWEKICAKICBwcmludChpKQogIH0gCn0KCiNjb21wYXJlIHJhc3RlcjphcmVhIGNhbGN1bGF0aW9uLCB0byBlcXVhbCBhcmVhIHN0aWxsIHVzaW5nIHJhc3Rlcjo6YXJlYSBmdW5jdGlvbiwgdG8gcmdlb3M6OmdBcmVhIGZ1bmN0aW9uIGZvciBwb2x5Z29ucwoKZ2dwbG90KGRhdGEgPSB3ZXN0X3BhY19zaGVsZl9hcmVhcykgKwogIGdlb21fcG9pbnQoYWVzKHggPSBsYXRpdHVkZV9zdGFydCwgeSA9IGFyZWFfcmdlb3NfZ0FyZWEpLCBjb2xvciA9ICJwdXJwbGUiLCBzaXplID0gMC41KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCB5ID0gYXJlYV9yYXN0ZXJhcmVhKSwgY29sb3IgPSAiZGFya2dyZWVuIiwgc2l6ZSA9IDAuNSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBsYXRpdHVkZV9zdGFydCwgeSA9IGFyZWFfZXF1YWxhcmVhcHJvaiksIGNvbG9yID0gInJlZCIsIHNpemUgPSAwLjUpICsKICBsYWJzKHg9cGFzdGUwKCJMYXRpdHVkZSAiLCJcdTAwQjAiLCJFIiksIHkgPSAiQXJlYSBrbV4yIikgKwogIHRoZW1lX2NsYXNzaWMoKQoKY29yKHdlc3RfcGFjX3NoZWxmX2FyZWFzWywzOjVdLCB1c2UgPSAiY29tcGxldGUub2JzIikKCnNhdmUod2VzdF9wYWNfc2hlbGZfYXJlYXMsIGZpbGUgPSAid2VzdF9wYWNfc2hlbGZfYXJlYXNfMmRlZ3JlZXMuUmRhdGEiKQpsb2FkKCJ3ZXN0X3BhY19zaGVsZl9hcmVhc18yZGVncmVlcy5SZGF0YSIpCgpgYGAKCkNsYXNzaWZ5IGJ5IHBlcmNlbnQgY2hhbmdlISEKCmJldHdlZW4gMSBhbmQgSW5mICUgY2hhbmdlID0gYXQgbGVhc3QgMiBmb2xkIGluY3JlYXNlIChub3RlIEkgY2xhc3NpZmllZCBhbnkgY2hhbmdlIGZyb20gMCB0byBzb21ldGhpbmcgYXMgTk9UIGEgc2lnbmlmaWNhbnQgY2hhbmdlLCBzaG91bGQgcmV0dXJuIHRvIHRoaXMgY29uY2VwdHVhbGx5KQoKYmV0d2VlbiAtMC41IGFuZCAtaW5mICUgY2hhbmdlID0gYXQgbGVhc3QgMiBmb2xkIGRlY3JlYXNlCgpiZXR3ZWVuIC0wLjQ5OSBhbmQgMC45OTkgPSBubyBzaWduaWZpY2FudCBjaGFuZ2UgCgpGb3IgYXJlYSBtZXRyaWMgaGVyZSwgSSB1c2VkIHRoZSByYXN0ZXI6OmFyZWEgZnVuY3Rpb24gYXBwbGllZCB0byB0aGUgcHJvamVjdGVkIHNoYXBlZmlsZQpgYGB7ciBwZXJjZW50IHNoaWZ0IHdlc3Rlcm4gcGFjaWZpY30Kd2VzdF9wYWNfc2hlbGZfYXJlYXNbLCBwZXJjZW50X2NoYW5nZSA6PSAoYXJlYV9yYXN0ZXJhcmVhLWRhdGEudGFibGU6OnNoaWZ0KGFyZWFfcmFzdGVyYXJlYSwgdHlwZSA9ICJsYWciKSkvZGF0YS50YWJsZTo6c2hpZnQoYXJlYV9yYXN0ZXJhcmVhLCB0eXBlID0gImxhZyIpXVssYXJlYV8xMDAwcyA6PSBhcmVhX3Jhc3RlcmFyZWEvMTAwMF0KCndlc3RfcGFjX3NoZWxmX2FyZWFzWyxoZW1pc3BoZXJlIDo9IGlmZWxzZShsYXRpdHVkZV9lbmQ+MCwibm9ydGgiLCJzb3V0aCIpXQoKd2VzdF9wYWNfc2hlbGZfYXJlYXNbLCBjaGFuZ2VfYWJvdmVfMmZvbGQgOj0gaWZlbHNlKChwZXJjZW50X2NoYW5nZT49MSAmIHBlcmNlbnRfY2hhbmdlPEluZiksIDEsIGlmZWxzZShwZXJjZW50X2NoYW5nZTw9LTAuNSwgLTEsIDApKV0KCndlc3RfcGFjX3NoZWxmX2FyZWFzX2hpZ2hsaWdodCA8LSB3ZXN0X3BhY19zaGVsZl9hcmVhc1tjaGFuZ2VfYWJvdmVfMmZvbGQgIT0gMCxdCgp3ZXN0X3BhY19zaGVsZl9hcmVhc19zdGF0cyA8LSB0YWJsZSh3ZXN0X3BhY19zaGVsZl9hcmVhc1ssLihjaGFuZ2VfYWJvdmVfMmZvbGQsaGVtaXNwaGVyZSldKQpgYGAKCk1vZGVsIGNoYW5nZSBmb3Igc291dGhlcm4gYW5kIG5vcnRoZXJuIGhlbWlzcGhlcmUKYGBge3IgbW9kZWwgbm9ydGhlcm4gdmVyc3VzIHNvdXRoZXJuIHdlc3Rlcm4gcGFjaWZpY30KI2FkZCBtaWQgbGF0aXR1ZGUKd2VzdF9wYWNfc2hlbGZfYXJlYXNbLGxhdGl0dWRlX21pZCA6PSBhYnMoKGxhdGl0dWRlX2VuZCtsYXRpdHVkZV9zdGFydCkvMildCgp3ZXN0X3BhY19ub3J0aF9tb2QgPC0gbG0oZGF0YSA9IHdlc3RfcGFjX3NoZWxmX2FyZWFzW2hlbWlzcGhlcmUgPT0gIm5vcnRoIl0sIGFyZWFfcmFzdGVyYXJlYSB+IGxhdGl0dWRlX21pZCkKc3VtbWFyeSh3ZXN0X3BhY19ub3J0aF9tb2QpCgp3ZXN0X3BhY19zb3V0aF9tb2QgPC0gbG0oZGF0YSA9IHdlc3RfcGFjX3NoZWxmX2FyZWFzW2hlbWlzcGhlcmUgPT0gInNvdXRoIl0sIGFyZWFfcmFzdGVyYXJlYSB+IGxhdGl0dWRlX21pZCkKc3VtbWFyeSh3ZXN0X3BhY19zb3V0aF9tb2QpCgpgYGAKCgpFYXN0ZXJuIFBhY2lmaWMKCmVhc3RfcGFjX3NwZGZfc2hpZnQKCmBgYHtyIHNwbGl0IHJhc3RlciBmb3IgZWFzdGVybiBwYWNpZmljfQpub3J0aF9leHRlbnQgPC0gYyh4bWluKGVhc3RfcGFjX3NwZGZfc2hpZnRfbWFza18xcyksIHhtYXgoZWFzdF9wYWNfc3BkZl9zaGlmdF9tYXNrXzFzKSwgMCwgeW1heChlYXN0X3BhY19zcGRmX3NoaWZ0X21hc2tfMXMpKQpzb3V0aF9leHRlbnQgPC0gYyh4bWluKGVhc3RfcGFjX3NwZGZfc2hpZnRfbWFza18xcyksIHhtYXgoZWFzdF9wYWNfc3BkZl9zaGlmdF9tYXNrXzFzKSwgeW1pbihlYXN0X3BhY19zcGRmX3NoaWZ0X21hc2tfMXMpLCAwKQoKI2Nyb3AgZWFzdF9wYWMgcmFzdGVyIGFib3ZlIGFuZCBiZWxvdyAwCmVhc3RfcGFjX3NwZGZfc2hpZnRfYWdnX25vcnRoIDwtIGNyb3AoZWFzdF9wYWNfc3BkZl9zaGlmdF9tYXNrXzFzLCBleHRlbnQobm9ydGhfZXh0ZW50KSkKCmVhc3RfcGFjX3NwZGZfc2hpZnRfYWdnX3NvdXRoIDwtIGNyb3AoZWFzdF9wYWNfc3BkZl9zaGlmdF9tYXNrXzFzLCBleHRlbnQoc291dGhfZXh0ZW50KSkKCiN1bmZvcnR1bmF0ZWx5LCBJIHRoaW5rIEkgbWF5IGhhdmUgdG8ganVzdCBkbyB0aGlzIG1hbnVhbGx5ICh1Z2x5LCBJIGtub3cpCgojYWxsIGNodW5rcyBmb3IgZWFzdCBwYWNpZmljCmVhc3RfcGFjX25vcnRoX2xhdGl0dWRlcyA8LSBzZXEoMCwgeW1heChlYXN0X3BhY19zcGRmX3NoaWZ0X21hc2tfMXMpLCBieSA9IDIpCmVhc3RfcGFjX3NvdXRoX2xhdGl0dWRlcyA8LSBzZXEoMCwgeW1pbihlYXN0X3BhY19zcGRmX3NoaWZ0X21hc2tfMXMpLCBieSA9IC0yKQoKI3NldHVwIGRhdGEgdGFibGUgdG8gcG9wdWxhdGUgaW4gbG9vcCwgc3VidHJhY3Rpbmcgb25lIHRvIGFsbG93IGZvciBiaW5zCmVhc3RfcGFjX3NoZWxmX2FyZWFzIDwtIGFzLmRhdGEudGFibGUobWF0cml4KG5yb3cgPSAobGVuZ3RoKGVhc3RfcGFjX25vcnRoX2xhdGl0dWRlcyktMStsZW5ndGgoZWFzdF9wYWNfc291dGhfbGF0aXR1ZGVzKS0xKSkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCmVhc3RfcGFjX3NoZWxmX2FyZWFzWywgbGF0aXR1ZGVfc3RhcnQgOj0gYXMubnVtZXJpYyhWMSldWywgbGF0aXR1ZGVfZW5kIDo9IGFzLm51bWVyaWMoVjEpXVssIGFyZWFfcmFzdGVyYXJlYSA6PSBhcy5udW1lcmljKFYxKV1bLCBhcmVhX2VxdWFsYXJlYXByb2ogOj0gYXMubnVtZXJpYyhWMSldWywgYXJlYV9yZ2Vvc19nQXJlYSA6PSBhcy5udW1lcmljKFYxKV1bLCBWMSA6PSBOVUxMXQoKI2xvb3AgZm9yIG5vcnRoCmZvciAoaSBpbiAxOihsZW5ndGgoZWFzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xKSkgewogICNzZXR0aW5nIHVwIGV4dGVudCBmb3Igc2xpY2luZyBieSBtaW4gYW5kIG1heCBsb25naXR1ZGVzLCBhbmQgaSB0byBpKzEgbGF0aXR1ZGVzCiAgbm9ydGhfZXh0ZW50IDwtIGMoeG1pbihlYXN0X3BhY19zcGRmX3NoaWZ0X21hc2tfMXMpLCB4bWF4KGVhc3RfcGFjX3NwZGZfc2hpZnRfbWFza18xcyksIGVhc3RfcGFjX25vcnRoX2xhdGl0dWRlc1tpXSwgZWFzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzW2krMV0pCiAgCiAgI2Nyb3AgcmFzdGVyIHNlZ2VtZW50IGJhc2VkIG9uIGJpbiBleHRlbnQKICBzZWdtZW50X25vcnRoIDwtIGNyb3AoZWFzdF9wYWNfc3BkZl9zaGlmdF9tYXNrXzFzLCBleHRlbnQobm9ydGhfZXh0ZW50KSkKICAKICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIGxhdGl0dWRpbmFsIGJpbgogIGVhc3RfcGFjX3NoZWxmX2FyZWFzW2ksICJsYXRpdHVkZV9zdGFydCJdIDwtIGVhc3RfcGFjX25vcnRoX2xhdGl0dWRlc1tpXQogIGVhc3RfcGFjX3NoZWxmX2FyZWFzW2ksICJsYXRpdHVkZV9lbmQiXSA8LSBlYXN0X3BhY19ub3J0aF9sYXRpdHVkZXNbaSsxXQogIAogIGlmKGFsbChpcy5uYSh2YWx1ZXMoc2VnbWVudF9ub3J0aCkpKSkgeyAjaWYgdGhlcmUncyBubyBzaGVsZiBhcmVhIHdpdGhpbiBhIGJpbiwgYWxsIGFyZWEgPSAwCiAgICAKICBlYXN0X3BhY19zaGVsZl9hcmVhc1tpLCAiYXJlYV9lcXVhbGFyZWFwcm9qIl0gPC0gMAogIGVhc3RfcGFjX3NoZWxmX2FyZWFzW2ksICJhcmVhX3Jhc3RlcmFyZWEiXSA8LSAwCiAgZWFzdF9wYWNfc2hlbGZfYXJlYXNbaSwgImFyZWFfcmdlb3NfZ0FyZWEiXSA8LSAwCgogIAogIHByaW50KGkpCiAgICAKICB9IGVsc2UgeyAjaWYgdGhlcmUgaXMgc2hlbGYgYXJlYSB3aXRoaW4gdGhlIGJpbiwgY2FsY3VsYXRlIGFyZWEgb2Ygc2xpY2UKICAKICAgICNyYXN0ZXIgYXJlYSBjYWxjdWxhdGlvbgogICAgICAjZ2V0IHNpemVzIG9mIGFsbCBjZWxscyBpbiByYXN0ZXIgW2ttMl0KICAgIGNlbGxfc2l6ZV9yYXN0ZXI8LWFyZWEoc2VnbWVudF9ub3J0aCwgbmEucm09VFJVRSwgd2VpZ2h0cz1GQUxTRSkKICAgIAogICAgI2RlbGV0ZSBOQXMgZnJvbSB2ZWN0b3Igb2YgYWxsIHJhc3RlciBjZWxscwogICAgY2VsbF9zaXplX3Jhc3RlcjwtY2VsbF9zaXplX3Jhc3RlclshaXMubmEoc2VnbWVudF9ub3J0aCldCiAgICAKICAgICNjb21wdXRlIGFyZWEgb2YgYWxsIGNlbGxzIGluIGdlb19yYXN0ZXIKICAgICNmdWxsIGFyZWEgICAgICAgICAgPC0gdG90YWwgIyBncmlkIGNlbGxzICAgICAqIG1lZGlhbiBjZWxsIGFyZWEgKHVzaW5nIG1lZGlhbiBjYXJlcyBsZXNzIGFib3V0IGV4dHJlbWUgdmFsdWVzKQogICAgc2VnbWVudF9hcmVhX3Jhc3RlciA8LSBsZW5ndGgoY2VsbF9zaXplX3Jhc3RlcikqbWVkaWFuKGNlbGxfc2l6ZV9yYXN0ZXIpICNpbiBrbV4yCiAgICAKICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIHJhc3RlciBhcmVhCiAgZWFzdF9wYWNfc2hlbGZfYXJlYXNbaSwgImFyZWFfcmFzdGVyYXJlYSJdIDwtIHNlZ21lbnRfYXJlYV9yYXN0ZXIKICAgIAogICNjb252ZXJ0IHRvIHNwYXRpYWwgcG9seWdvbnMgdG8gY2hlY2sgYXJlYSBjYWxjdWxhdGlvbnMKICAgIAogICNjb252ZXJ0IHNlZ21lbnQgZnJvbSByYXN0ZXIgdG8gcG9seWdvbiwgZWFjaCBjZWxsIGZyb20gdGhlIHJhc3RlciBpcyBhbiBpbmRlcGVuZGVudCBwb2x5Z29uLCAoZGlzc29sdmUgbWVhbnMgYWxsIGNlbGxzIHdpdGggYSB2YWx1ZSBvZiAxIGFyZSBhIHNpbmdsZSBwb2x5Z29uIGlmIGNvbm5lY3RlZCkKICBzZWdtZW50X25vcnRoLnNwIDwtIHJhc3RlclRvUG9seWdvbnMoc2VnbWVudF9ub3J0aCwgZGlzc29sdmUgPSBUKQogIAogICMgIElmIHggaXMgYSBTcGF0aWFsUG9seWdvbnMqIG9iamVjdDogYXJlYSBvZiBlYWNoIHNwYXRpYWwgb2JqZWN0IGluIHNxdWFyZWQgbWV0ZXJzIGlmIHRoZSBDUlMgaXMgbG9uZ2l0dWRlL2xhdGl0dWRlLCBvciBpbiBzcXVhcmVkIG1hcCB1bml0cyAodHlwaWNhbGx5IG1ldGVyKQogIAogICAgI3Byb2plY3QgdG8gZXF1YWwgZWFydGggYXJlYSBwcm9qZWN0aW9uCiAgc2VnbWVudF9ub3J0aC5zcC5FQSA8LSBzcFRyYW5zZm9ybShzZWdtZW50X25vcnRoLnNwLCBDUlNvYmogPSBlcXVhbGFyZWFwcm9qZWN0aW9uKQoKICAjY2FsY3VsYXRlIGFyZWEgb2YgdGhlIHNwYXRpYWwgb2JqZWN0IGluIG1eMgogICAgcG9seWdvbl9zaXplLnNwIDwtIGFyZWEoc2VnbWVudF9ub3J0aC5zcC5FQSkKCiAgICAjY29udmVydCBmcm9tIG1eMiB0byBrbV4yCiAgICBzZWdtZW50X2FyZWFfZXF1YWxhcmVhIDwtIHBvbHlnb25fc2l6ZS5zcC8xZTYKCiAgICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIHBvbHlnb24gYXJlYSB1c2luZyByYXN0ZXIgY2FsY3VsYXRpb24KICBlYXN0X3BhY19zaGVsZl9hcmVhc1tpLCAiYXJlYV9lcXVhbGFyZWFwcm9qIl0gPC0gc2VnbWVudF9hcmVhX2VxdWFsYXJlYQogIAogICNhbmQgdGhlbiBwbGFpbiBhbmQgc2ltcGxlIGFsc28gdXNpbmcgcmdlb3M6OmdBcmVhCiAgCiAgYXJlYV9yZ2Vvc19nQXJlYSA8LSBnQXJlYShzZWdtZW50X25vcnRoLnNwLkVBKS8xZTYKICAKICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIHBvbHlnb24gYXJlYSB1c2luZyByZWdlb3MgY2FsY3VsYXRpb24KICBlYXN0X3BhY19zaGVsZl9hcmVhc1tpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtICAgYXJlYV9yZ2Vvc19nQXJlYQogIAogIHByaW50KGkpCiAgfSAKfQoKI2xvb3AgZm9yIHNvdXRoCmZvciAoaSBpbiAxOihsZW5ndGgoZWFzdF9wYWNfc291dGhfbGF0aXR1ZGVzKS0xKSkgewogIHNvdXRoX2V4dGVudCA8LSBjKHhtaW4oZWFzdF9wYWNfc3BkZl9zaGlmdF9tYXNrXzFzKSwgeG1heChlYXN0X3BhY19zcGRmX3NoaWZ0X21hc2tfMXMpLCBlYXN0X3BhY19zb3V0aF9sYXRpdHVkZXNbaSsxXSwgZWFzdF9wYWNfc291dGhfbGF0aXR1ZGVzW2ldKSAjb3JkZXI9IHhtaW4sIHhtYXgsIHltaW4sIHltYXgpCiAgCiAgI3Jhc3RlciBzZWdtZW50CiAgc2VnbWVudF9zb3V0aCA8LSBjcm9wKGVhc3RfcGFjX3NwZGZfc2hpZnRfbWFza18xcywgZXh0ZW50KHNvdXRoX2V4dGVudCkpCiAgCiAgI2FkZCBsYXRpdHVkZSBiaW4gaW5mbyB0byBkYXRhIHRhYmxlCiAgICBlYXN0X3BhY19zaGVsZl9hcmVhc1tpKyhsZW5ndGgoZWFzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImxhdGl0dWRlX3N0YXJ0Il0gPC0gZWFzdF9wYWNfc291dGhfbGF0aXR1ZGVzW2ldCiAgZWFzdF9wYWNfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKGVhc3RfcGFjX25vcnRoX2xhdGl0dWRlcyktMSksICJsYXRpdHVkZV9lbmQiXSA8LSBlYXN0X3BhY19zb3V0aF9sYXRpdHVkZXNbaSsxXQogIAogIAogIGlmKGFsbChpcy5uYSh2YWx1ZXMoc2VnbWVudF9zb3V0aCkpKSkgeyAjaWYgdGhlcmUncyBubyBzaGVsZiBhcmVhIHdpdGhpbiBhIGJpbiwgbWVhbmluZyB0aGVyZSdzIG5vIHNoZWxmIGFyZWEgYXQgdGhhdCBsYXRpdHVkZQoKICBlYXN0X3BhY19zaGVsZl9hcmVhc1tpKyhsZW5ndGgoZWFzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfZXF1YWxhcmVhcHJvaiJdIDwtIDAKICBlYXN0X3BhY19zaGVsZl9hcmVhc1tpKyhsZW5ndGgoZWFzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfcmFzdGVyYXJlYSJdIDwtIDAKICAgIGVhc3RfcGFjX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X3BhY19ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtIDAKICAKICBwcmludChpKQogICAgCiAgfSBlbHNlIHsKICAKICAgICNyYXN0ZXIgYXJlYSBjYWxjdWxhdGlvbgogICAgICAjZ2V0IHNpemVzIG9mIGFsbCBjZWxscyBpbiByYXN0ZXIgW2ttMl0KICAgIGNlbGxfc2l6ZV9yYXN0ZXI8LWFyZWEoc2VnbWVudF9zb3V0aCwgbmEucm09VFJVRSwgd2VpZ2h0cz1GQUxTRSkKICAgIAogICAgI2RlbGV0ZSBOQXMgZnJvbSB2ZWN0b3Igb2YgYWxsIHJhc3RlciBjZWxscwogICAgY2VsbF9zaXplX3Jhc3RlcjwtY2VsbF9zaXplX3Jhc3RlclshaXMubmEoc2VnbWVudF9zb3V0aCldCiAgICAjY29tcHV0ZSBhcmVhIG9mIGFsbCBjZWxscyBpbiBnZW9fcmFzdGVyCiAgICAjZnVsbCBhcmVhICAgICAgICAgIDwtIHRvdGFsICMgZ3JpZCBjZWxscyAgICAgKiBtZWRpYW4gY2VsbCBhcmVhICh1c2luZyBtZWRpYW4gY2FyZXMgbGVzcyBhYm91dCBleHRyZW1lIHZhbHVlcykKICAgIHNlZ21lbnRfYXJlYV9yYXN0ZXIgPC0gbGVuZ3RoKGNlbGxfc2l6ZV9yYXN0ZXIpKm1lZGlhbihjZWxsX3NpemVfcmFzdGVyKQogICAgCiAgI3BvcHVsYXRlIGRhdGEgdGFibGUgd2l0aCByYXN0ZXIgYXJlYQogIGVhc3RfcGFjX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X3BhY19ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yYXN0ZXJhcmVhIl0gPC0gc2VnbWVudF9hcmVhX3Jhc3RlcgogICAgCiAgCiAgI2NvbnZlcnQgdG8gc3BhdGlhbCBwb2x5Z29ucyB0byBjaGVjayBhcmVhIGNhbGN1bGF0aW9ucwogICAgCiAgI2NvbnZlcnQgc2VnbWVudCBmcm9tIHJhc3RlciB0byBwb2x5Z29uLCBlYWNoIGNlbGwgZnJvbSB0aGUgcmFzdGVyIGlzIGFuIGluZGVwZW5kZW50IHBvbHlnb24sIChkaXNzb2x2ZSBtZWFucyBhbGwgY2VsbHMgd2l0aCBhIHZhbHVlIG9mIDEgYXJlIGEgc2luZ2xlIHBvbHlnb24gaWYgY29ubmVjdGVkKQogIHNlZ21lbnRfc291dGguc3AgPC0gcmFzdGVyVG9Qb2x5Z29ucyhzZWdtZW50X3NvdXRoLCBkaXNzb2x2ZSA9IFQpCiAgCiAgIyAgSWYgeCBpcyBhIFNwYXRpYWxQb2x5Z29ucyogb2JqZWN0OiBhcmVhIG9mIGVhY2ggc3BhdGlhbCBvYmplY3QgaW4gc3F1YXJlZCBtZXRlcnMgaWYgdGhlIENSUyBpcyBsb25naXR1ZGUvbGF0aXR1ZGUsIG9yIGluIHNxdWFyZWQgbWFwIHVuaXRzICh0eXBpY2FsbHkgbWV0ZXIpCiAgCiAgICAjcHJvamVjdCB0byBlcXVhbCBlYXJ0aCBhcmVhIHByb2plY3Rpb24KICBzZWdtZW50X3NvdXRoLnNwLkVBIDwtIHNwVHJhbnNmb3JtKHNlZ21lbnRfc291dGguc3AsIENSU29iaiA9IGVxdWFsYXJlYXByb2plY3Rpb24pCgogICNjYWxjdWxhdGUgYXJlYSBvZiB0aGUgc3BhdGlhbCBvYmplY3QgaW4gbV4yCiAgICBwb2x5Z29uX3NpemUuc3AgPC0gYXJlYShzZWdtZW50X3NvdXRoLnNwLkVBKQoKICAgICNjb252ZXJ0IGZyb20gbV4yIHRvIGttXjIKICAgIHNlZ21lbnRfYXJlYV9lcXVhbGFyZWEgPC0gcG9seWdvbl9zaXplLnNwLzFlNgoKICAgICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggYXJlYSBvZiBwb2x5Z29uIGZyb20gcmFzdGVyOjphcmVhIGZ1bmN0aW9uCiAgZWFzdF9wYWNfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKGVhc3RfcGFjX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSBzZWdtZW50X2FyZWFfZXF1YWxhcmVhCiAgCiAgI2FuZCB0aGVuIHBsYWluIGFuZCBzaW1wbGUgYWxzbyB1c2luZyByZ2Vvczo6Z0FyZWEKICAKICBhcmVhX3JnZW9zX2dBcmVhIDwtIGdBcmVhKHNlZ21lbnRfc291dGguc3AuRUEpLzFlNgogIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIGZyb20gcmdlb3MgYXJlYSBjYWxjdWxhdGlvbiBmb3IgcHJvamVjdGVkIHBvbHlnb24KICBlYXN0X3BhY19zaGVsZl9hcmVhc1tpKyhsZW5ndGgoZWFzdF9wYWNfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfcmdlb3NfZ0FyZWEiXSA8LSAgIGFyZWFfcmdlb3NfZ0FyZWEKICAKICBwcmludChpKQogIH0gCn0KCiNjb21wYXJlIHJhc3RlcjphcmVhIGNhbGN1bGF0aW9uLCB0byBlcXVhbCBhcmVhIHN0aWxsIHVzaW5nIHJhc3Rlcjo6YXJlYSBmdW5jdGlvbiwgdG8gcmdlb3M6OmdBcmVhIGZ1bmN0aW9uIGZvciBwb2x5Z29ucwoKZ2dwbG90KGRhdGEgPSBlYXN0X3BhY19zaGVsZl9hcmVhcykgKwogIGdlb21fcG9pbnQoYWVzKHggPSBsYXRpdHVkZV9zdGFydCwgeSA9IGFyZWFfcmdlb3NfZ0FyZWEpLCBjb2xvciA9ICJwdXJwbGUiLCBzaXplID0gMC41KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCB5ID0gYXJlYV9yYXN0ZXJhcmVhKSwgY29sb3IgPSAiZGFya2dyZWVuIiwgc2l6ZSA9IDAuNSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBsYXRpdHVkZV9zdGFydCwgeSA9IGFyZWFfZXF1YWxhcmVhcHJvaiksIGNvbG9yID0gInJlZCIsIHNpemUgPSAwLjUpICsKICBsYWJzKHg9cGFzdGUwKCJMYXRpdHVkZSAiLCJcdTAwQjAiLCJFIiksIHkgPSAiQXJlYSBrbV4yIikgKwogIHRoZW1lX2NsYXNzaWMoKQoKY29yKGVhc3RfcGFjX3NoZWxmX2FyZWFzWywzOjVdLCB1c2UgPSAiY29tcGxldGUub2JzIikKCnNhdmUoZWFzdF9wYWNfc2hlbGZfYXJlYXMsIGZpbGUgPSAiZWFzdF9wYWNfc2hlbGZfYXJlYXNfMmRlZ3JlZXMuUmRhdGEiKQoKYGBgCgpgYGB7ciBwZXJjZW50IHNoaWZ0IGVhc3Rlcm4gcGFjaWZpY30KZWFzdF9wYWNfc2hlbGZfYXJlYXNbLCBwZXJjZW50X2NoYW5nZSA6PSAoYXJlYV9yYXN0ZXJhcmVhLWRhdGEudGFibGU6OnNoaWZ0KGFyZWFfcmFzdGVyYXJlYSwgdHlwZSA9ICJsYWciKSkvZGF0YS50YWJsZTo6c2hpZnQoYXJlYV9yYXN0ZXJhcmVhLCB0eXBlID0gImxhZyIpXVssYXJlYV8xMDAwcyA6PSBhcmVhX3Jhc3RlcmFyZWEvMTAwMF0KCmVhc3RfcGFjX3NoZWxmX2FyZWFzWyxoZW1pc3BoZXJlIDo9IGlmZWxzZShsYXRpdHVkZV9lbmQ+MCwibm9ydGgiLCJzb3V0aCIpXQoKZWFzdF9wYWNfc2hlbGZfYXJlYXNbLCBjaGFuZ2VfYWJvdmVfMmZvbGQgOj0gaWZlbHNlKChwZXJjZW50X2NoYW5nZT49MSAmIHBlcmNlbnRfY2hhbmdlPEluZiksIDEsIGlmZWxzZShwZXJjZW50X2NoYW5nZTw9LTAuNSwgLTEsIDApKV0KCmVhc3RfcGFjX3NoZWxmX2FyZWFzX2hpZ2hsaWdodCA8LSBlYXN0X3BhY19zaGVsZl9hcmVhc1tjaGFuZ2VfYWJvdmVfMmZvbGQgIT0gMCxdCgplYXN0X3BhY19zaGVsZl9hcmVhc19zdGF0cyA8LSB0YWJsZShlYXN0X3BhY19zaGVsZl9hcmVhc1ssLihjaGFuZ2VfYWJvdmVfMmZvbGQsIGhlbWlzcGhlcmUpXSkKYGBgCgpNb2RlbCBjaGFuZ2UgZm9yIHNvdXRoZXJuIGFuZCBub3J0aGVybiBoZW1pc3BoZXJlCmBgYHtyIG1vZGVsIG5vcnRoZXJuIHZlcnN1cyBzb3V0aGVybiBlYXN0ZXJuIHBhY2lmaWN9CiNhZGQgbWlkIGxhdGl0dWRlCmVhc3RfcGFjX3NoZWxmX2FyZWFzWyxsYXRpdHVkZV9taWQgOj0gYWJzKChsYXRpdHVkZV9lbmQrbGF0aXR1ZGVfc3RhcnQpLzIpXQoKZWFzdF9wYWNfbm9ydGhfbW9kIDwtIGxtKGRhdGEgPSBlYXN0X3BhY19zaGVsZl9hcmVhc1toZW1pc3BoZXJlID09ICJub3J0aCJdLCBhcmVhX3Jhc3RlcmFyZWEgfiBsYXRpdHVkZV9taWQpCnN1bW1hcnkoZWFzdF9wYWNfbm9ydGhfbW9kKQoKZWFzdF9wYWNfc291dGhfbW9kIDwtIGxtKGRhdGEgPSBlYXN0X3BhY19zaGVsZl9hcmVhc1toZW1pc3BoZXJlID09ICJzb3V0aCJdLCBhcmVhX3Jhc3RlcmFyZWEgfiBsYXRpdHVkZV9taWQpCnN1bW1hcnkoZWFzdF9wYWNfc291dGhfbW9kKQpgYGAKCldlc3Rlcm4gQXRsYW50aWMKCndlc3RfYXRsX3NwZGZfbWFzawoKYGBge3Igc3BsaXQgcmFzdGVyIGZvciB3ZXN0ZXJuIGF0bGFudGljfQpub3J0aF9leHRlbnQgPC0gYyh4bWluKHdlc3RfYXRsX3NwZGZfbWFza18xcyksIHhtYXgod2VzdF9hdGxfc3BkZl9tYXNrXzFzKSwgMCwgeW1heCh3ZXN0X2F0bF9zcGRmX21hc2tfMXMpKQpzb3V0aF9leHRlbnQgPC0gYyh4bWluKHdlc3RfYXRsX3NwZGZfbWFza18xcyksIHhtYXgod2VzdF9hdGxfc3BkZl9tYXNrXzFzKSwgeW1pbih3ZXN0X2F0bF9zcGRmX21hc2tfMXMpLCAwKQoKI2Nyb3Agd2VzdF9hdGwgcmFzdGVyIGFib3ZlIGFuZCBiZWxvdyAwCndlc3RfYXRsX3NwZGZfc2hpZnRfYWdnX25vcnRoIDwtIGNyb3Aod2VzdF9hdGxfc3BkZl9tYXNrXzFzLCBleHRlbnQobm9ydGhfZXh0ZW50KSkKCndlc3RfYXRsX3NwZGZfc2hpZnRfYWdnX3NvdXRoIDwtIGNyb3Aod2VzdF9hdGxfc3BkZl9tYXNrXzFzLCBleHRlbnQoc291dGhfZXh0ZW50KSkKCiN1bmZvcnR1bmF0ZWx5LCBJIHRoaW5rIEkgbWF5IGhhdmUgdG8ganVzdCBkbyB0aGlzIG1hbnVhbGx5ICh1Z2x5LCBJIGtub3cpCgojYWxsIGNodW5rcyBmb3Igd2VzdCBhdGxhbnRpYwp3ZXN0X2F0bF9ub3J0aF9sYXRpdHVkZXMgPC0gc2VxKDAsIHltYXgod2VzdF9hdGxfc3BkZl9tYXNrXzFzKSwgYnkgPSAyKQp3ZXN0X2F0bF9zb3V0aF9sYXRpdHVkZXMgPC0gc2VxKDAsIHltaW4od2VzdF9hdGxfc3BkZl9tYXNrXzFzKSwgYnkgPSAtMikKCiNzZXR1cCBkYXRhIHRhYmxlIHRvIHBvcHVsYXRlIGluIGxvb3AsIHN1YnRyYWN0aW5nIG9uZSB0byBhbGxvdyBmb3IgYmlucwp3ZXN0X2F0bF9zaGVsZl9hcmVhcyA8LSBhcy5kYXRhLnRhYmxlKG1hdHJpeChucm93ID0gKGxlbmd0aCh3ZXN0X2F0bF9ub3J0aF9sYXRpdHVkZXMpLTErbGVuZ3RoKHdlc3RfYXRsX3NvdXRoX2xhdGl0dWRlcyktMSkpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAp3ZXN0X2F0bF9zaGVsZl9hcmVhc1ssIGxhdGl0dWRlX3N0YXJ0IDo9IGFzLm51bWVyaWMoVjEpXVssIGxhdGl0dWRlX2VuZCA6PSBhcy5udW1lcmljKFYxKV1bLCBhcmVhX3Jhc3RlcmFyZWEgOj0gYXMubnVtZXJpYyhWMSldWywgYXJlYV9lcXVhbGFyZWFwcm9qIDo9IGFzLm51bWVyaWMoVjEpXVssIGFyZWFfcmdlb3NfZ0FyZWEgOj0gYXMubnVtZXJpYyhWMSldWywgVjEgOj0gTlVMTF0KCiNsb29wIGZvciBub3J0aApmb3IgKGkgaW4gMToobGVuZ3RoKHdlc3RfYXRsX25vcnRoX2xhdGl0dWRlcyktMSkpIHsKICAjc2V0dGluZyB1cCBleHRlbnQgZm9yIHNsaWNpbmcgYnkgbWluIGFuZCBtYXggbG9uZ2l0dWRlcywgYW5kIGkgdG8gaSsxIGxhdGl0dWRlcwogIG5vcnRoX2V4dGVudCA8LSBjKHhtaW4od2VzdF9hdGxfc3BkZl9tYXNrXzFzKSwgeG1heCh3ZXN0X2F0bF9zcGRmX21hc2tfMXMpLCB3ZXN0X2F0bF9ub3J0aF9sYXRpdHVkZXNbaV0sIHdlc3RfYXRsX25vcnRoX2xhdGl0dWRlc1tpKzFdKQogIAogICNjcm9wIHJhc3RlciBzZWdlbWVudCBiYXNlZCBvbiBiaW4gZXh0ZW50CiAgc2VnbWVudF9ub3J0aCA8LSBjcm9wKHdlc3RfYXRsX3NwZGZfbWFza18xcywgZXh0ZW50KG5vcnRoX2V4dGVudCkpCiAgCiAgI3BvcHVsYXRlIGRhdGEgdGFibGUgd2l0aCBsYXRpdHVkaW5hbCBiaW4KICB3ZXN0X2F0bF9zaGVsZl9hcmVhc1tpLCAibGF0aXR1ZGVfc3RhcnQiXSA8LSB3ZXN0X2F0bF9ub3J0aF9sYXRpdHVkZXNbaV0KICB3ZXN0X2F0bF9zaGVsZl9hcmVhc1tpLCAibGF0aXR1ZGVfZW5kIl0gPC0gd2VzdF9hdGxfbm9ydGhfbGF0aXR1ZGVzW2krMV0KICAKICBpZihhbGwoaXMubmEodmFsdWVzKHNlZ21lbnRfbm9ydGgpKSkpIHsgI2lmIHRoZXJlJ3Mgbm8gc2hlbGYgYXJlYSB3aXRoaW4gYSBiaW4sIGFsbCBhcmVhID0gMAogICAgCiAgd2VzdF9hdGxfc2hlbGZfYXJlYXNbaSwgImFyZWFfZXF1YWxhcmVhcHJvaiJdIDwtIDAKICB3ZXN0X2F0bF9zaGVsZl9hcmVhc1tpLCAiYXJlYV9yYXN0ZXJhcmVhIl0gPC0gMAogIHdlc3RfYXRsX3NoZWxmX2FyZWFzW2ksICJhcmVhX3JnZW9zX2dBcmVhIl0gPC0gMAoKICBwcmludChpKQogICAgCiAgfSBlbHNlIHsgI2lmIHRoZXJlIGlzIHNoZWxmIGFyZWEgd2l0aGluIHRoZSBiaW4sIGNhbGN1bGF0ZSBhcmVhIG9mIHNsaWNlCiAgCiAgICAjcmFzdGVyIGFyZWEgY2FsY3VsYXRpb24KICAgICAgI2dldCBzaXplcyBvZiBhbGwgY2VsbHMgaW4gcmFzdGVyIFtrbTJdCiAgICBjZWxsX3NpemVfcmFzdGVyPC1hcmVhKHNlZ21lbnRfbm9ydGgsIG5hLnJtPVRSVUUsIHdlaWdodHM9RkFMU0UpCiAgICAKICAgICNkZWxldGUgTkFzIGZyb20gdmVjdG9yIG9mIGFsbCByYXN0ZXIgY2VsbHMKICAgIGNlbGxfc2l6ZV9yYXN0ZXI8LWNlbGxfc2l6ZV9yYXN0ZXJbIWlzLm5hKHNlZ21lbnRfbm9ydGgpXQogICAgCiAgICAjY29tcHV0ZSBhcmVhIG9mIGFsbCBjZWxscyBpbiBnZW9fcmFzdGVyCiAgICAjZnVsbCBhcmVhICAgICAgICAgIDwtIHRvdGFsICMgZ3JpZCBjZWxscyAgICAgKiBtZWRpYW4gY2VsbCBhcmVhICh1c2luZyBtZWRpYW4gY2FyZXMgbGVzcyBhYm91dCBleHRyZW1lIHZhbHVlcykKICAgIHNlZ21lbnRfYXJlYV9yYXN0ZXIgPC0gbGVuZ3RoKGNlbGxfc2l6ZV9yYXN0ZXIpKm1lZGlhbihjZWxsX3NpemVfcmFzdGVyKSAjaW4ga21eMgogICAgCiAgI3BvcHVsYXRlIGRhdGEgdGFibGUgd2l0aCByYXN0ZXIgYXJlYQogIHdlc3RfYXRsX3NoZWxmX2FyZWFzW2ksICJhcmVhX3Jhc3RlcmFyZWEiXSA8LSBzZWdtZW50X2FyZWFfcmFzdGVyCiAgICAKICAjY29udmVydCB0byBzcGF0aWFsIHBvbHlnb25zIHRvIGNoZWNrIGFyZWEgY2FsY3VsYXRpb25zCiAgICAKICAjY29udmVydCBzZWdtZW50IGZyb20gcmFzdGVyIHRvIHBvbHlnb24sIGVhY2ggY2VsbCBmcm9tIHRoZSByYXN0ZXIgaXMgYW4gaW5kZXBlbmRlbnQgcG9seWdvbiwgKGRpc3NvbHZlIG1lYW5zIGFsbCBjZWxscyB3aXRoIGEgdmFsdWUgb2YgMSBhcmUgYSBzaW5nbGUgcG9seWdvbiBpZiBjb25uZWN0ZWQpCiAgc2VnbWVudF9ub3J0aC5zcCA8LSByYXN0ZXJUb1BvbHlnb25zKHNlZ21lbnRfbm9ydGgsIGRpc3NvbHZlID0gVCkKICAKICAjICBJZiB4IGlzIGEgU3BhdGlhbFBvbHlnb25zKiBvYmplY3Q6IGFyZWEgb2YgZWFjaCBzcGF0aWFsIG9iamVjdCBpbiBzcXVhcmVkIG1ldGVycyBpZiB0aGUgQ1JTIGlzIGxvbmdpdHVkZS9sYXRpdHVkZSwgb3IgaW4gc3F1YXJlZCBtYXAgdW5pdHMgKHR5cGljYWxseSBtZXRlcikKICAKICAgICNwcm9qZWN0IHRvIGVxdWFsIGVhcnRoIGFyZWEgcHJvamVjdGlvbgogIHNlZ21lbnRfbm9ydGguc3AuRUEgPC0gc3BUcmFuc2Zvcm0oc2VnbWVudF9ub3J0aC5zcCwgQ1JTb2JqID0gZXF1YWxhcmVhcHJvamVjdGlvbikKCiAgI2NhbGN1bGF0ZSBhcmVhIG9mIHRoZSBzcGF0aWFsIG9iamVjdCBpbiBtXjIKICAgIHBvbHlnb25fc2l6ZS5zcCA8LSBhcmVhKHNlZ21lbnRfbm9ydGguc3AuRUEpCgogICAgI2NvbnZlcnQgZnJvbSBtXjIgdG8ga21eMgogICAgc2VnbWVudF9hcmVhX2VxdWFsYXJlYSA8LSBwb2x5Z29uX3NpemUuc3AvMWU2CgogICAgI3BvcHVsYXRlIGRhdGEgdGFibGUgd2l0aCBwb2x5Z29uIGFyZWEgdXNpbmcgcmFzdGVyIGNhbGN1bGF0aW9uCiAgd2VzdF9hdGxfc2hlbGZfYXJlYXNbaSwgImFyZWFfZXF1YWxhcmVhcHJvaiJdIDwtIHNlZ21lbnRfYXJlYV9lcXVhbGFyZWEKICAKICAjYW5kIHRoZW4gcGxhaW4gYW5kIHNpbXBsZSBhbHNvIHVzaW5nIHJnZW9zOjpnQXJlYQogIAogIGFyZWFfcmdlb3NfZ0FyZWEgPC0gZ0FyZWEoc2VnbWVudF9ub3J0aC5zcC5FQSkvMWU2CiAgCiAgI3BvcHVsYXRlIGRhdGEgdGFibGUgd2l0aCBwb2x5Z29uIGFyZWEgdXNpbmcgcmVnZW9zIGNhbGN1bGF0aW9uCiAgd2VzdF9hdGxfc2hlbGZfYXJlYXNbaSwgImFyZWFfcmdlb3NfZ0FyZWEiXSA8LSAgIGFyZWFfcmdlb3NfZ0FyZWEKICAKICBwcmludChpKQogIH0gCn0KCiNsb29wIGZvciBzb3V0aApmb3IgKGkgaW4gMToobGVuZ3RoKHdlc3RfYXRsX3NvdXRoX2xhdGl0dWRlcyktMSkpIHsKICBzb3V0aF9leHRlbnQgPC0gYyh4bWluKHdlc3RfYXRsX3NwZGZfbWFza18xcyksIHhtYXgod2VzdF9hdGxfc3BkZl9tYXNrXzFzKSwgd2VzdF9hdGxfc291dGhfbGF0aXR1ZGVzW2krMV0sIHdlc3RfYXRsX3NvdXRoX2xhdGl0dWRlc1tpXSkgI29yZGVyPSB4bWluLCB4bWF4LCB5bWluLCB5bWF4KQogIAogICNyYXN0ZXIgc2VnbWVudAogIHNlZ21lbnRfc291dGggPC0gY3JvcCh3ZXN0X2F0bF9zcGRmX21hc2tfMXMsIGV4dGVudChzb3V0aF9leHRlbnQpKQogIAogICNhZGQgbGF0aXR1ZGUgYmluIGluZm8gdG8gZGF0YSB0YWJsZQogICAgd2VzdF9hdGxfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKHdlc3RfYXRsX25vcnRoX2xhdGl0dWRlcyktMSksICJsYXRpdHVkZV9zdGFydCJdIDwtIHdlc3RfYXRsX3NvdXRoX2xhdGl0dWRlc1tpXQogIHdlc3RfYXRsX3NoZWxmX2FyZWFzW2krKGxlbmd0aCh3ZXN0X2F0bF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAibGF0aXR1ZGVfZW5kIl0gPC0gd2VzdF9hdGxfc291dGhfbGF0aXR1ZGVzW2krMV0KICAKICAKICBpZihhbGwoaXMubmEodmFsdWVzKHNlZ21lbnRfc291dGgpKSkpIHsgI2lmIHRoZXJlJ3Mgbm8gc2hlbGYgYXJlYSB3aXRoaW4gYSBiaW4sIG1lYW5pbmcgdGhlcmUncyBubyBzaGVsZiBhcmVhIGF0IHRoYXQgbGF0aXR1ZGUKCiAgd2VzdF9hdGxfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKHdlc3RfYXRsX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSAwCiAgd2VzdF9hdGxfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKHdlc3RfYXRsX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX3Jhc3RlcmFyZWEiXSA8LSAwCiAgICB3ZXN0X2F0bF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9hdGxfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfcmdlb3NfZ0FyZWEiXSA8LSAwCiAgCiAgcHJpbnQoaSkKICAgIAogIH0gZWxzZSB7CiAgCiAgICAjcmFzdGVyIGFyZWEgY2FsY3VsYXRpb24KICAgICAgI2dldCBzaXplcyBvZiBhbGwgY2VsbHMgaW4gcmFzdGVyIFtrbTJdCiAgICBjZWxsX3NpemVfcmFzdGVyPC1hcmVhKHNlZ21lbnRfc291dGgsIG5hLnJtPVRSVUUsIHdlaWdodHM9RkFMU0UpCiAgICAKICAgICNkZWxldGUgTkFzIGZyb20gdmVjdG9yIG9mIGFsbCByYXN0ZXIgY2VsbHMKICAgIGNlbGxfc2l6ZV9yYXN0ZXI8LWNlbGxfc2l6ZV9yYXN0ZXJbIWlzLm5hKHNlZ21lbnRfc291dGgpXQogICAgI2NvbXB1dGUgYXJlYSBvZiBhbGwgY2VsbHMgaW4gZ2VvX3Jhc3RlcgogICAgI2Z1bGwgYXJlYSAgICAgICAgICA8LSB0b3RhbCAjIGdyaWQgY2VsbHMgICAgICogbWVkaWFuIGNlbGwgYXJlYSAodXNpbmcgbWVkaWFuIGNhcmVzIGxlc3MgYWJvdXQgZXh0cmVtZSB2YWx1ZXMpCiAgICBzZWdtZW50X2FyZWFfcmFzdGVyIDwtIGxlbmd0aChjZWxsX3NpemVfcmFzdGVyKSptZWRpYW4oY2VsbF9zaXplX3Jhc3RlcikKICAgIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcmFzdGVyIGFyZWEKICB3ZXN0X2F0bF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9hdGxfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfcmFzdGVyYXJlYSJdIDwtIHNlZ21lbnRfYXJlYV9yYXN0ZXIKICAgIAogIAogICNjb252ZXJ0IHRvIHNwYXRpYWwgcG9seWdvbnMgdG8gY2hlY2sgYXJlYSBjYWxjdWxhdGlvbnMKICAgIAogICNjb252ZXJ0IHNlZ21lbnQgZnJvbSByYXN0ZXIgdG8gcG9seWdvbiwgZWFjaCBjZWxsIGZyb20gdGhlIHJhc3RlciBpcyBhbiBpbmRlcGVuZGVudCBwb2x5Z29uLCAoZGlzc29sdmUgbWVhbnMgYWxsIGNlbGxzIHdpdGggYSB2YWx1ZSBvZiAxIGFyZSBhIHNpbmdsZSBwb2x5Z29uIGlmIGNvbm5lY3RlZCkKICBzZWdtZW50X3NvdXRoLnNwIDwtIHJhc3RlclRvUG9seWdvbnMoc2VnbWVudF9zb3V0aCwgZGlzc29sdmUgPSBUKQogIAogICMgIElmIHggaXMgYSBTcGF0aWFsUG9seWdvbnMqIG9iamVjdDogYXJlYSBvZiBlYWNoIHNwYXRpYWwgb2JqZWN0IGluIHNxdWFyZWQgbWV0ZXJzIGlmIHRoZSBDUlMgaXMgbG9uZ2l0dWRlL2xhdGl0dWRlLCBvciBpbiBzcXVhcmVkIG1hcCB1bml0cyAodHlwaWNhbGx5IG1ldGVyKQogIAogICAgI3Byb2plY3QgdG8gZXF1YWwgZWFydGggYXJlYSBwcm9qZWN0aW9uCiAgc2VnbWVudF9zb3V0aC5zcC5FQSA8LSBzcFRyYW5zZm9ybShzZWdtZW50X3NvdXRoLnNwLCBDUlNvYmogPSBlcXVhbGFyZWFwcm9qZWN0aW9uKQoKICAjY2FsY3VsYXRlIGFyZWEgb2YgdGhlIHNwYXRpYWwgb2JqZWN0IGluIG1eMgogICAgcG9seWdvbl9zaXplLnNwIDwtIGFyZWEoc2VnbWVudF9zb3V0aC5zcC5FQSkKCiAgICAjY29udmVydCBmcm9tIG1eMiB0byBrbV4yCiAgICBzZWdtZW50X2FyZWFfZXF1YWxhcmVhIDwtIHBvbHlnb25fc2l6ZS5zcC8xZTYKCiAgICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIGFyZWEgb2YgcG9seWdvbiBmcm9tIHJhc3Rlcjo6YXJlYSBmdW5jdGlvbgogIHdlc3RfYXRsX3NoZWxmX2FyZWFzW2krKGxlbmd0aCh3ZXN0X2F0bF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9lcXVhbGFyZWFwcm9qIl0gPC0gc2VnbWVudF9hcmVhX2VxdWFsYXJlYQogIAogICNhbmQgdGhlbiBwbGFpbiBhbmQgc2ltcGxlIGFsc28gdXNpbmcgcmdlb3M6OmdBcmVhCiAgCiAgYXJlYV9yZ2Vvc19nQXJlYSA8LSBnQXJlYShzZWdtZW50X3NvdXRoLnNwLkVBKS8xZTYKICAKICAjcG9wdWxhdGUgZGF0YSB0YWJsZSBmcm9tIHJnZW9zIGFyZWEgY2FsY3VsYXRpb24gZm9yIHByb2plY3RlZCBwb2x5Z29uCiAgd2VzdF9hdGxfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKHdlc3RfYXRsX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX3JnZW9zX2dBcmVhIl0gPC0gICBhcmVhX3JnZW9zX2dBcmVhCiAgCiAgcHJpbnQoaSkKICB9IAp9CgojY29tcGFyZSByYXN0ZXI6YXJlYSBjYWxjdWxhdGlvbiwgdG8gZXF1YWwgYXJlYSBzdGlsbCB1c2luZyByYXN0ZXI6OmFyZWEgZnVuY3Rpb24sIHRvIHJnZW9zOjpnQXJlYSBmdW5jdGlvbiBmb3IgcG9seWdvbnMKCmdncGxvdChkYXRhID0gd2VzdF9hdGxfc2hlbGZfYXJlYXMpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gbGF0aXR1ZGVfc3RhcnQsIHkgPSBhcmVhX3JnZW9zX2dBcmVhKSwgY29sb3IgPSAicHVycGxlIiwgc2l6ZSA9IDAuNSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBsYXRpdHVkZV9zdGFydCwgeSA9IGFyZWFfcmFzdGVyYXJlYSksIGNvbG9yID0gImRhcmtncmVlbiIsIHNpemUgPSAwLjUpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gbGF0aXR1ZGVfc3RhcnQsIHkgPSBhcmVhX2VxdWFsYXJlYXByb2opLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMC41KSArCiAgbGFicyh4PXBhc3RlMCgiTGF0aXR1ZGUgIiwiXHUwMEIwIiwiRSIpLCB5ID0gIkFyZWEga21eMiIpICsKICB0aGVtZV9jbGFzc2ljKCkKCmNvcih3ZXN0X2F0bF9zaGVsZl9hcmVhc1ssMzo1XSwgdXNlID0gImNvbXBsZXRlLm9icyIpCgpgYGAKCmBgYHtyIHBlcmNlbnQgc2hpZnQgd2VzdGVybiBhdGxhbnRpY30Kd2VzdF9hdGxfc2hlbGZfYXJlYXNbLCBwZXJjZW50X2NoYW5nZSA6PSAoYXJlYV9yYXN0ZXJhcmVhLWRhdGEudGFibGU6OnNoaWZ0KGFyZWFfcmFzdGVyYXJlYSwgdHlwZSA9ICJsYWciKSkvZGF0YS50YWJsZTo6c2hpZnQoYXJlYV9yYXN0ZXJhcmVhLCB0eXBlID0gImxhZyIpXVssYXJlYV8xMDAwcyA6PSBhcmVhX3Jhc3RlcmFyZWEvMTAwMF0KCndlc3RfYXRsX3NoZWxmX2FyZWFzWyxoZW1pc3BoZXJlIDo9IGlmZWxzZShsYXRpdHVkZV9lbmQ+MCwibm9ydGgiLCJzb3V0aCIpXQoKd2VzdF9hdGxfc2hlbGZfYXJlYXNbLCBjaGFuZ2VfYWJvdmVfMmZvbGQgOj0gaWZlbHNlKChwZXJjZW50X2NoYW5nZT49MSAmIHBlcmNlbnRfY2hhbmdlPEluZiksIDEsIGlmZWxzZShwZXJjZW50X2NoYW5nZTw9LTAuNSwgLTEsIDApKV0KCndlc3RfYXRsX3NoZWxmX2FyZWFzX2hpZ2hsaWdodCA8LSB3ZXN0X2F0bF9zaGVsZl9hcmVhc1tjaGFuZ2VfYWJvdmVfMmZvbGQgIT0gMCxdCgp3ZXN0X2F0bF9zaGVsZl9hcmVhc19zdGF0cyA8LSB0YWJsZSh3ZXN0X2F0bF9zaGVsZl9hcmVhc1ssLihjaGFuZ2VfYWJvdmVfMmZvbGQsIGhlbWlzcGhlcmUpXSkKYGBgCgpNb2RlbCBjaGFuZ2UgZm9yIHNvdXRoZXJuIGFuZCBub3J0aGVybiBoZW1pc3BoZXJlCmBgYHtyIG1vZGVsIG5vcnRoZXJuIHZlcnN1cyBzb3V0aGVybiB3ZXN0ZXJuIGF0bGFudGljfQojYWRkIG1pZCBsYXRpdHVkZQp3ZXN0X2F0bF9zaGVsZl9hcmVhc1ssbGF0aXR1ZGVfbWlkIDo9IGFicygobGF0aXR1ZGVfZW5kK2xhdGl0dWRlX3N0YXJ0KS8yKV0KCndlc3RfYXRsX25vcnRoX21vZCA8LSBsbShkYXRhID0gd2VzdF9hdGxfc2hlbGZfYXJlYXNbaGVtaXNwaGVyZSA9PSAibm9ydGgiXSwgYXJlYV9yYXN0ZXJhcmVhIH4gbGF0aXR1ZGVfbWlkKQpzdW1tYXJ5KHdlc3RfYXRsX25vcnRoX21vZCkKCndlc3RfYXRsX3NvdXRoX21vZCA8LSBsbShkYXRhID0gd2VzdF9hdGxfc2hlbGZfYXJlYXNbaGVtaXNwaGVyZSA9PSAic291dGgiXSwgYXJlYV9yYXN0ZXJhcmVhIH4gbGF0aXR1ZGVfbWlkKQpzdW1tYXJ5KHdlc3RfYXRsX3NvdXRoX21vZCkKYGBgCgpFYXN0ZXJuIEF0bGFudGljCgplYXN0X2F0bF9zcGRmX25vYnVmX21hc2sKCmBgYHtyIHNwbGl0IHJhc3RlciBmb3IgZWFzdGVybiBhdGxhbnRpY30Kbm9ydGhfZXh0ZW50IDwtIGMoeG1pbihlYXN0X2F0bF9zcGRmX25vYnVmX21hc2tfMXMpLCB4bWF4KGVhc3RfYXRsX3NwZGZfbm9idWZfbWFza18xcyksIDAsIHltYXgoZWFzdF9hdGxfc3BkZl9ub2J1Zl9tYXNrXzFzKSkKc291dGhfZXh0ZW50IDwtIGMoeG1pbihlYXN0X2F0bF9zcGRmX25vYnVmX21hc2tfMXMpLCB4bWF4KGVhc3RfYXRsX3NwZGZfbm9idWZfbWFza18xcyksIHltaW4oZWFzdF9hdGxfc3BkZl9ub2J1Zl9tYXNrXzFzKSwgMCkKCiNjcm9wIGVhc3RfYXRsIHJhc3RlciBhYm92ZSBhbmQgYmVsb3cgMAplYXN0X2F0bF9zcGRmX3NoaWZ0X2FnZ19ub3J0aCA8LSBjcm9wKGVhc3RfYXRsX3NwZGZfbm9idWZfbWFza18xcywgZXh0ZW50KG5vcnRoX2V4dGVudCkpCgplYXN0X2F0bF9zcGRmX3NoaWZ0X2FnZ19zb3V0aCA8LSBjcm9wKGVhc3RfYXRsX3NwZGZfbm9idWZfbWFza18xcywgZXh0ZW50KHNvdXRoX2V4dGVudCkpCgojdW5mb3J0dW5hdGVseSwgSSB0aGluayBJIG1heSBoYXZlIHRvIGp1c3QgZG8gdGhpcyBtYW51YWxseSAodWdseSwgSSBrbm93KQoKI2FsbCBjaHVua3MgZm9yIGVhc3QgYXRsYW50aWMKZWFzdF9hdGxfbm9ydGhfbGF0aXR1ZGVzIDwtIHNlcSgwLCB5bWF4KGVhc3RfYXRsX3NwZGZfbm9idWZfbWFza18xcyksIGJ5ID0gMikKZWFzdF9hdGxfc291dGhfbGF0aXR1ZGVzIDwtIHNlcSgwLCB5bWluKGVhc3RfYXRsX3NwZGZfbm9idWZfbWFza18xcyksIGJ5ID0gLTIpCgojc2V0dXAgZGF0YSB0YWJsZSB0byBwb3B1bGF0ZSBpbiBsb29wLCBzdWJ0cmFjdGluZyBvbmUgdG8gYWxsb3cgZm9yIGJpbnMKZWFzdF9hdGxfc2hlbGZfYXJlYXMgPC0gYXMuZGF0YS50YWJsZShtYXRyaXgobnJvdyA9IChsZW5ndGgoZWFzdF9hdGxfbm9ydGhfbGF0aXR1ZGVzKS0xK2xlbmd0aChlYXN0X2F0bF9zb3V0aF9sYXRpdHVkZXMpLTEpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKZWFzdF9hdGxfc2hlbGZfYXJlYXNbLCBsYXRpdHVkZV9zdGFydCA6PSBhcy5udW1lcmljKFYxKV1bLCBsYXRpdHVkZV9lbmQgOj0gYXMubnVtZXJpYyhWMSldWywgYXJlYV9yYXN0ZXJhcmVhIDo9IGFzLm51bWVyaWMoVjEpXVssIGFyZWFfZXF1YWxhcmVhcHJvaiA6PSBhcy5udW1lcmljKFYxKV1bLCBhcmVhX3JnZW9zX2dBcmVhIDo9IGFzLm51bWVyaWMoVjEpXVssIFYxIDo9IE5VTExdCgojbG9vcCBmb3Igbm9ydGgKZm9yIChpIGluIDE6KGxlbmd0aChlYXN0X2F0bF9ub3J0aF9sYXRpdHVkZXMpLTEpKSB7CiAgI3NldHRpbmcgdXAgZXh0ZW50IGZvciBzbGljaW5nIGJ5IG1pbiBhbmQgbWF4IGxvbmdpdHVkZXMsIGFuZCBpIHRvIGkrMSBsYXRpdHVkZXMKICBub3J0aF9leHRlbnQgPC0gYyh4bWluKGVhc3RfYXRsX3NwZGZfbm9idWZfbWFza18xcyksIHhtYXgoZWFzdF9hdGxfc3BkZl9ub2J1Zl9tYXNrXzFzKSwgZWFzdF9hdGxfbm9ydGhfbGF0aXR1ZGVzW2ldLCBlYXN0X2F0bF9ub3J0aF9sYXRpdHVkZXNbaSsxXSkKICAKICAjY3JvcCByYXN0ZXIgc2VnZW1lbnQgYmFzZWQgb24gYmluIGV4dGVudAogIHNlZ21lbnRfbm9ydGggPC0gY3JvcChlYXN0X2F0bF9zcGRmX25vYnVmX21hc2tfMXMsIGV4dGVudChub3J0aF9leHRlbnQpKQogIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggbGF0aXR1ZGluYWwgYmluCiAgZWFzdF9hdGxfc2hlbGZfYXJlYXNbaSwgImxhdGl0dWRlX3N0YXJ0Il0gPC0gZWFzdF9hdGxfbm9ydGhfbGF0aXR1ZGVzW2ldCiAgZWFzdF9hdGxfc2hlbGZfYXJlYXNbaSwgImxhdGl0dWRlX2VuZCJdIDwtIGVhc3RfYXRsX25vcnRoX2xhdGl0dWRlc1tpKzFdCiAgCiAgaWYoYWxsKGlzLm5hKHZhbHVlcyhzZWdtZW50X25vcnRoKSkpKSB7ICNpZiB0aGVyZSdzIG5vIHNoZWxmIGFyZWEgd2l0aGluIGEgYmluLCBhbGwgYXJlYSA9IDAKICAgIAogIGVhc3RfYXRsX3NoZWxmX2FyZWFzW2ksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSAwCiAgZWFzdF9hdGxfc2hlbGZfYXJlYXNbaSwgImFyZWFfcmFzdGVyYXJlYSJdIDwtIDAKICBlYXN0X2F0bF9zaGVsZl9hcmVhc1tpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtIDAKCiAgCiAgcHJpbnQoaSkKICAgIAogIH0gZWxzZSB7ICNpZiB0aGVyZSBpcyBzaGVsZiBhcmVhIHdpdGhpbiB0aGUgYmluLCBjYWxjdWxhdGUgYXJlYSBvZiBzbGljZQogIAogICAgI3Jhc3RlciBhcmVhIGNhbGN1bGF0aW9uCiAgICAgICNnZXQgc2l6ZXMgb2YgYWxsIGNlbGxzIGluIHJhc3RlciBba20yXQogICAgY2VsbF9zaXplX3Jhc3RlcjwtYXJlYShzZWdtZW50X25vcnRoLCBuYS5ybT1UUlVFLCB3ZWlnaHRzPUZBTFNFKQogICAgCiAgICAjZGVsZXRlIE5BcyBmcm9tIHZlY3RvciBvZiBhbGwgcmFzdGVyIGNlbGxzCiAgICBjZWxsX3NpemVfcmFzdGVyPC1jZWxsX3NpemVfcmFzdGVyWyFpcy5uYShzZWdtZW50X25vcnRoKV0KICAgIAogICAgI2NvbXB1dGUgYXJlYSBvZiBhbGwgY2VsbHMgaW4gZ2VvX3Jhc3RlcgogICAgI2Z1bGwgYXJlYSAgICAgICAgICA8LSB0b3RhbCAjIGdyaWQgY2VsbHMgICAgICogbWVkaWFuIGNlbGwgYXJlYSAodXNpbmcgbWVkaWFuIGNhcmVzIGxlc3MgYWJvdXQgZXh0cmVtZSB2YWx1ZXMpCiAgICBzZWdtZW50X2FyZWFfcmFzdGVyIDwtIGxlbmd0aChjZWxsX3NpemVfcmFzdGVyKSptZWRpYW4oY2VsbF9zaXplX3Jhc3RlcikgI2luIGttXjIKICAgIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcmFzdGVyIGFyZWEKICBlYXN0X2F0bF9zaGVsZl9hcmVhc1tpLCAiYXJlYV9yYXN0ZXJhcmVhIl0gPC0gc2VnbWVudF9hcmVhX3Jhc3RlcgogICAgCiAgI2NvbnZlcnQgdG8gc3BhdGlhbCBwb2x5Z29ucyB0byBjaGVjayBhcmVhIGNhbGN1bGF0aW9ucwogICAgCiAgI2NvbnZlcnQgc2VnbWVudCBmcm9tIHJhc3RlciB0byBwb2x5Z29uLCBlYWNoIGNlbGwgZnJvbSB0aGUgcmFzdGVyIGlzIGFuIGluZGVwZW5kZW50IHBvbHlnb24sIChkaXNzb2x2ZSBtZWFucyBhbGwgY2VsbHMgd2l0aCBhIHZhbHVlIG9mIDEgYXJlIGEgc2luZ2xlIHBvbHlnb24gaWYgY29ubmVjdGVkKQogIHNlZ21lbnRfbm9ydGguc3AgPC0gcmFzdGVyVG9Qb2x5Z29ucyhzZWdtZW50X25vcnRoLCBkaXNzb2x2ZSA9IFQpCiAgCiAgIyAgSWYgeCBpcyBhIFNwYXRpYWxQb2x5Z29ucyogb2JqZWN0OiBhcmVhIG9mIGVhY2ggc3BhdGlhbCBvYmplY3QgaW4gc3F1YXJlZCBtZXRlcnMgaWYgdGhlIENSUyBpcyBsb25naXR1ZGUvbGF0aXR1ZGUsIG9yIGluIHNxdWFyZWQgbWFwIHVuaXRzICh0eXBpY2FsbHkgbWV0ZXIpCiAgCiAgICAjcHJvamVjdCB0byBlcXVhbCBlYXJ0aCBhcmVhIHByb2plY3Rpb24KICBzZWdtZW50X25vcnRoLnNwLkVBIDwtIHNwVHJhbnNmb3JtKHNlZ21lbnRfbm9ydGguc3AsIENSU29iaiA9IGVxdWFsYXJlYXByb2plY3Rpb24pCgogICNjYWxjdWxhdGUgYXJlYSBvZiB0aGUgc3BhdGlhbCBvYmplY3QgaW4gbV4yCiAgICBwb2x5Z29uX3NpemUuc3AgPC0gYXJlYShzZWdtZW50X25vcnRoLnNwLkVBKQoKICAgICNjb252ZXJ0IGZyb20gbV4yIHRvIGttXjIKICAgIHNlZ21lbnRfYXJlYV9lcXVhbGFyZWEgPC0gcG9seWdvbl9zaXplLnNwLzFlNgoKICAgICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcG9seWdvbiBhcmVhIHVzaW5nIHJhc3RlciBjYWxjdWxhdGlvbgogIGVhc3RfYXRsX3NoZWxmX2FyZWFzW2ksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSBzZWdtZW50X2FyZWFfZXF1YWxhcmVhCiAgCiAgI2FuZCB0aGVuIHBsYWluIGFuZCBzaW1wbGUgYWxzbyB1c2luZyByZ2Vvczo6Z0FyZWEKICAKICBhcmVhX3JnZW9zX2dBcmVhIDwtIGdBcmVhKHNlZ21lbnRfbm9ydGguc3AuRUEpLzFlNgogIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcG9seWdvbiBhcmVhIHVzaW5nIHJlZ2VvcyBjYWxjdWxhdGlvbgogIGVhc3RfYXRsX3NoZWxmX2FyZWFzW2ksICJhcmVhX3JnZW9zX2dBcmVhIl0gPC0gICBhcmVhX3JnZW9zX2dBcmVhCiAgCiAgcHJpbnQoaSkKICB9IAp9CgojbG9vcCBmb3Igc291dGgKZm9yIChpIGluIDE6KGxlbmd0aChlYXN0X2F0bF9zb3V0aF9sYXRpdHVkZXMpLTEpKSB7CiAgc291dGhfZXh0ZW50IDwtIGMoeG1pbihlYXN0X2F0bF9zcGRmX25vYnVmX21hc2tfMXMpLCB4bWF4KGVhc3RfYXRsX3NwZGZfbm9idWZfbWFza18xcyksIGVhc3RfYXRsX3NvdXRoX2xhdGl0dWRlc1tpKzFdLCBlYXN0X2F0bF9zb3V0aF9sYXRpdHVkZXNbaV0pICNvcmRlcj0geG1pbiwgeG1heCwgeW1pbiwgeW1heCkKICAKICAjcmFzdGVyIHNlZ21lbnQKICBzZWdtZW50X3NvdXRoIDwtIGNyb3AoZWFzdF9hdGxfc3BkZl9ub2J1Zl9tYXNrXzFzLCBleHRlbnQoc291dGhfZXh0ZW50KSkKICAKICAjYWRkIGxhdGl0dWRlIGJpbiBpbmZvIHRvIGRhdGEgdGFibGUKICAgIGVhc3RfYXRsX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X2F0bF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAibGF0aXR1ZGVfc3RhcnQiXSA8LSBlYXN0X2F0bF9zb3V0aF9sYXRpdHVkZXNbaV0KICBlYXN0X2F0bF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgoZWFzdF9hdGxfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImxhdGl0dWRlX2VuZCJdIDwtIGVhc3RfYXRsX3NvdXRoX2xhdGl0dWRlc1tpKzFdCiAgCiAgCiAgaWYoYWxsKGlzLm5hKHZhbHVlcyhzZWdtZW50X3NvdXRoKSkpKSB7ICNpZiB0aGVyZSdzIG5vIHNoZWxmIGFyZWEgd2l0aGluIGEgYmluLCBtZWFuaW5nIHRoZXJlJ3Mgbm8gc2hlbGYgYXJlYSBhdCB0aGF0IGxhdGl0dWRlCgogIGVhc3RfYXRsX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X2F0bF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9lcXVhbGFyZWFwcm9qIl0gPC0gMAogIGVhc3RfYXRsX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X2F0bF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yYXN0ZXJhcmVhIl0gPC0gMAogICAgZWFzdF9hdGxfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKGVhc3RfYXRsX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX3JnZW9zX2dBcmVhIl0gPC0gMAogIAogIHByaW50KGkpCiAgICAKICB9IGVsc2UgewogIAogICAgI3Jhc3RlciBhcmVhIGNhbGN1bGF0aW9uCiAgICAgICNnZXQgc2l6ZXMgb2YgYWxsIGNlbGxzIGluIHJhc3RlciBba20yXQogICAgY2VsbF9zaXplX3Jhc3RlcjwtYXJlYShzZWdtZW50X3NvdXRoLCBuYS5ybT1UUlVFLCB3ZWlnaHRzPUZBTFNFKQogICAgCiAgICAjZGVsZXRlIE5BcyBmcm9tIHZlY3RvciBvZiBhbGwgcmFzdGVyIGNlbGxzCiAgICBjZWxsX3NpemVfcmFzdGVyPC1jZWxsX3NpemVfcmFzdGVyWyFpcy5uYShzZWdtZW50X3NvdXRoKV0KICAgICNjb21wdXRlIGFyZWEgb2YgYWxsIGNlbGxzIGluIGdlb19yYXN0ZXIKICAgICNmdWxsIGFyZWEgICAgICAgICAgPC0gdG90YWwgIyBncmlkIGNlbGxzICAgICAqIG1lZGlhbiBjZWxsIGFyZWEgKHVzaW5nIG1lZGlhbiBjYXJlcyBsZXNzIGFib3V0IGV4dHJlbWUgdmFsdWVzKQogICAgc2VnbWVudF9hcmVhX3Jhc3RlciA8LSBsZW5ndGgoY2VsbF9zaXplX3Jhc3RlcikqbWVkaWFuKGNlbGxfc2l6ZV9yYXN0ZXIpCiAgICAKICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIHJhc3RlciBhcmVhCiAgZWFzdF9hdGxfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKGVhc3RfYXRsX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX3Jhc3RlcmFyZWEiXSA8LSBzZWdtZW50X2FyZWFfcmFzdGVyCiAgICAKICAKICAjY29udmVydCB0byBzcGF0aWFsIHBvbHlnb25zIHRvIGNoZWNrIGFyZWEgY2FsY3VsYXRpb25zCiAgICAKICAjY29udmVydCBzZWdtZW50IGZyb20gcmFzdGVyIHRvIHBvbHlnb24sIGVhY2ggY2VsbCBmcm9tIHRoZSByYXN0ZXIgaXMgYW4gaW5kZXBlbmRlbnQgcG9seWdvbiwgKGRpc3NvbHZlIG1lYW5zIGFsbCBjZWxscyB3aXRoIGEgdmFsdWUgb2YgMSBhcmUgYSBzaW5nbGUgcG9seWdvbiBpZiBjb25uZWN0ZWQpCiAgc2VnbWVudF9zb3V0aC5zcCA8LSByYXN0ZXJUb1BvbHlnb25zKHNlZ21lbnRfc291dGgsIGRpc3NvbHZlID0gVCkKICAKICAjICBJZiB4IGlzIGEgU3BhdGlhbFBvbHlnb25zKiBvYmplY3Q6IGFyZWEgb2YgZWFjaCBzcGF0aWFsIG9iamVjdCBpbiBzcXVhcmVkIG1ldGVycyBpZiB0aGUgQ1JTIGlzIGxvbmdpdHVkZS9sYXRpdHVkZSwgb3IgaW4gc3F1YXJlZCBtYXAgdW5pdHMgKHR5cGljYWxseSBtZXRlcikKICAKICAgICNwcm9qZWN0IHRvIGVxdWFsIGVhcnRoIGFyZWEgcHJvamVjdGlvbgogIHNlZ21lbnRfc291dGguc3AuRUEgPC0gc3BUcmFuc2Zvcm0oc2VnbWVudF9zb3V0aC5zcCwgQ1JTb2JqID0gZXF1YWxhcmVhcHJvamVjdGlvbikKCiAgI2NhbGN1bGF0ZSBhcmVhIG9mIHRoZSBzcGF0aWFsIG9iamVjdCBpbiBtXjIKICAgIHBvbHlnb25fc2l6ZS5zcCA8LSBhcmVhKHNlZ21lbnRfc291dGguc3AuRUEpCgogICAgI2NvbnZlcnQgZnJvbSBtXjIgdG8ga21eMgogICAgc2VnbWVudF9hcmVhX2VxdWFsYXJlYSA8LSBwb2x5Z29uX3NpemUuc3AvMWU2CgogICAgI3BvcHVsYXRlIGRhdGEgdGFibGUgd2l0aCBhcmVhIG9mIHBvbHlnb24gZnJvbSByYXN0ZXI6OmFyZWEgZnVuY3Rpb24KICBlYXN0X2F0bF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgoZWFzdF9hdGxfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfZXF1YWxhcmVhcHJvaiJdIDwtIHNlZ21lbnRfYXJlYV9lcXVhbGFyZWEKICAKICAjYW5kIHRoZW4gcGxhaW4gYW5kIHNpbXBsZSBhbHNvIHVzaW5nIHJnZW9zOjpnQXJlYQogIAogIGFyZWFfcmdlb3NfZ0FyZWEgPC0gZ0FyZWEoc2VnbWVudF9zb3V0aC5zcC5FQSkvMWU2CiAgCiAgI3BvcHVsYXRlIGRhdGEgdGFibGUgZnJvbSByZ2VvcyBhcmVhIGNhbGN1bGF0aW9uIGZvciBwcm9qZWN0ZWQgcG9seWdvbgogIGVhc3RfYXRsX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X2F0bF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtICAgYXJlYV9yZ2Vvc19nQXJlYQogIAogIHByaW50KGkpCiAgfSAKfQoKI2NvbXBhcmUgcmFzdGVyOmFyZWEgY2FsY3VsYXRpb24sIHRvIGVxdWFsIGFyZWEgc3RpbGwgdXNpbmcgcmFzdGVyOjphcmVhIGZ1bmN0aW9uLCB0byByZ2Vvczo6Z0FyZWEgZnVuY3Rpb24gZm9yIHBvbHlnb25zCgpnZ3Bsb3QoZGF0YSA9IGVhc3RfYXRsX3NoZWxmX2FyZWFzKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCB5ID0gYXJlYV9yZ2Vvc19nQXJlYSksIGNvbG9yID0gInB1cnBsZSIsIHNpemUgPSAwLjUpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gbGF0aXR1ZGVfc3RhcnQsIHkgPSBhcmVhX3Jhc3RlcmFyZWEpLCBjb2xvciA9ICJkYXJrZ3JlZW4iLCBzaXplID0gMC41KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCB5ID0gYXJlYV9lcXVhbGFyZWFwcm9qKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDAuNSkgKwogIGxhYnMoeD1wYXN0ZTAoIkxhdGl0dWRlICIsIlx1MDBCMCIsIkUiKSwgeSA9ICJBcmVhIGttXjIiKSArCiAgdGhlbWVfY2xhc3NpYygpCgpjb3IoZWFzdF9hdGxfc2hlbGZfYXJlYXNbLDM6NV0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQoKYGBgCgpgYGB7ciBwZXJjZW50IHNoaWZ0IGVhc3Rlcm4gYXRsYW50aWN9CmVhc3RfYXRsX3NoZWxmX2FyZWFzWywgcGVyY2VudF9jaGFuZ2UgOj0gKGFyZWFfcmFzdGVyYXJlYS1kYXRhLnRhYmxlOjpzaGlmdChhcmVhX3Jhc3RlcmFyZWEsIHR5cGUgPSAibGFnIikpL2RhdGEudGFibGU6OnNoaWZ0KGFyZWFfcmFzdGVyYXJlYSwgdHlwZSA9ICJsYWciKV1bLGFyZWFfMTAwMHMgOj0gYXJlYV9yYXN0ZXJhcmVhLzEwMDBdCgplYXN0X2F0bF9zaGVsZl9hcmVhc1ssaGVtaXNwaGVyZSA6PSBpZmVsc2UobGF0aXR1ZGVfZW5kPjAsIm5vcnRoIiwic291dGgiKV0KCmVhc3RfYXRsX3NoZWxmX2FyZWFzWywgY2hhbmdlX2Fib3ZlXzJmb2xkIDo9IGlmZWxzZSgocGVyY2VudF9jaGFuZ2U+PTEgJiBwZXJjZW50X2NoYW5nZTxJbmYpLCAxLCBpZmVsc2UocGVyY2VudF9jaGFuZ2U8PS0wLjUsIC0xLCAwKSldCgplYXN0X2F0bF9zaGVsZl9hcmVhc19oaWdobGlnaHQgPC0gZWFzdF9hdGxfc2hlbGZfYXJlYXNbY2hhbmdlX2Fib3ZlXzJmb2xkICE9IDAsXQoKZWFzdF9hdGxfc2hlbGZfYXJlYXNfc3RhdHMgPC0gdGFibGUoZWFzdF9hdGxfc2hlbGZfYXJlYXNbLC4oY2hhbmdlX2Fib3ZlXzJmb2xkLCBoZW1pc3BoZXJlKV0pCmBgYAoKTW9kZWwgY2hhbmdlIGZvciBzb3V0aGVybiBhbmQgbm9ydGhlcm4gaGVtaXNwaGVyZQpgYGB7ciBtb2RlbCBub3J0aGVybiB2ZXJzdXMgc291dGhlcm4gZWFzdGVybiBhdGxhbnRpY30KI2FkZCBtaWQgbGF0aXR1ZGUKZWFzdF9hdGxfc2hlbGZfYXJlYXNbLGxhdGl0dWRlX21pZCA6PSBhYnMoKGxhdGl0dWRlX2VuZCtsYXRpdHVkZV9zdGFydCkvMildCgplYXN0X2F0bF9ub3J0aF9tb2QgPC0gbG0oZGF0YSA9IGVhc3RfYXRsX3NoZWxmX2FyZWFzW2hlbWlzcGhlcmUgPT0gIm5vcnRoIl0sIGFyZWFfcmFzdGVyYXJlYSB+IGxhdGl0dWRlX21pZCkKc3VtbWFyeShlYXN0X2F0bF9ub3J0aF9tb2QpCgplYXN0X2F0bF9zb3V0aF9tb2QgPC0gbG0oZGF0YSA9IGVhc3RfYXRsX3NoZWxmX2FyZWFzW2hlbWlzcGhlcmUgPT0gInNvdXRoIl0sIGFyZWFfcmFzdGVyYXJlYSB+IGxhdGl0dWRlX21pZCkKc3VtbWFyeShlYXN0X2F0bF9zb3V0aF9tb2QpCmBgYAoKV2VzdGVybiBJbmRpYW4KCndlc3RfaW5kX3NwZGZfbWFzawoKYGBge3Igc3BsaXQgcmFzdGVyIGZvciB3ZXN0ZXJuIGluZGlhbn0Kbm9ydGhfZXh0ZW50IDwtIGMoeG1pbih3ZXN0X2luZF9zcGRmX21hc2tfMXMpLCB4bWF4KHdlc3RfaW5kX3NwZGZfbWFza18xcyksIDAsIHltYXgod2VzdF9pbmRfc3BkZl9tYXNrXzFzKSkKc291dGhfZXh0ZW50IDwtIGMoeG1pbih3ZXN0X2luZF9zcGRmX21hc2tfMXMpLCB4bWF4KHdlc3RfaW5kX3NwZGZfbWFza18xcyksIHltaW4od2VzdF9pbmRfc3BkZl9tYXNrXzFzKSwgMCkKCiNjcm9wIHdlc3RfaW5kIHJhc3RlciBhYm92ZSBhbmQgYmVsb3cgMAp3ZXN0X2luZF9zcGRmX3NoaWZ0X2FnZ19ub3J0aCA8LSBjcm9wKHdlc3RfaW5kX3NwZGZfbWFza18xcywgZXh0ZW50KG5vcnRoX2V4dGVudCkpCgp3ZXN0X2luZF9zcGRmX3NoaWZ0X2FnZ19zb3V0aCA8LSBjcm9wKHdlc3RfaW5kX3NwZGZfbWFza18xcywgZXh0ZW50KHNvdXRoX2V4dGVudCkpCgojdW5mb3J0dW5hdGVseSwgSSB0aGluayBJIG1heSBoYXZlIHRvIGp1c3QgZG8gdGhpcyBtYW51YWxseSAodWdseSwgSSBrbm93KQoKI2FsbCBjaHVua3MgZm9yIHdlc3QgaW5kaWFuCndlc3RfaW5kX25vcnRoX2xhdGl0dWRlcyA8LSBzZXEoMCwgeW1heCh3ZXN0X2luZF9zcGRmX21hc2tfMXMpLCBieSA9IDIpCndlc3RfaW5kX3NvdXRoX2xhdGl0dWRlcyA8LSBzZXEoMCwgeW1pbih3ZXN0X2luZF9zcGRmX21hc2tfMXMpLCBieSA9IC0yKQoKI3NldHVwIGRhdGEgdGFibGUgdG8gcG9wdWxhdGUgaW4gbG9vcCwgc3VidHJhY3Rpbmcgb25lIHRvIGFsbG93IGZvciBiaW5zCndlc3RfaW5kX3NoZWxmX2FyZWFzIDwtIGFzLmRhdGEudGFibGUobWF0cml4KG5yb3cgPSAobGVuZ3RoKHdlc3RfaW5kX25vcnRoX2xhdGl0dWRlcyktMStsZW5ndGgod2VzdF9pbmRfc291dGhfbGF0aXR1ZGVzKS0xKSkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCndlc3RfaW5kX3NoZWxmX2FyZWFzWywgbGF0aXR1ZGVfc3RhcnQgOj0gYXMubnVtZXJpYyhWMSldWywgbGF0aXR1ZGVfZW5kIDo9IGFzLm51bWVyaWMoVjEpXVssIGFyZWFfcmFzdGVyYXJlYSA6PSBhcy5udW1lcmljKFYxKV1bLCBhcmVhX2VxdWFsYXJlYXByb2ogOj0gYXMubnVtZXJpYyhWMSldWywgYXJlYV9yZ2Vvc19nQXJlYSA6PSBhcy5udW1lcmljKFYxKV1bLCBWMSA6PSBOVUxMXQoKI2xvb3AgZm9yIG5vcnRoCmZvciAoaSBpbiAxOihsZW5ndGgod2VzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzKS0xKSkgewogICNzZXR0aW5nIHVwIGV4dGVudCBmb3Igc2xpY2luZyBieSBtaW4gYW5kIG1heCBsb25naXR1ZGVzLCBhbmQgaSB0byBpKzEgbGF0aXR1ZGVzCiAgbm9ydGhfZXh0ZW50IDwtIGMoeG1pbih3ZXN0X2luZF9zcGRmX21hc2tfMXMpLCB4bWF4KHdlc3RfaW5kX3NwZGZfbWFza18xcyksIHdlc3RfaW5kX25vcnRoX2xhdGl0dWRlc1tpXSwgd2VzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzW2krMV0pCiAgCiAgI2Nyb3AgcmFzdGVyIHNlZ2VtZW50IGJhc2VkIG9uIGJpbiBleHRlbnQKICBzZWdtZW50X25vcnRoIDwtIGNyb3Aod2VzdF9pbmRfc3BkZl9tYXNrXzFzLCBleHRlbnQobm9ydGhfZXh0ZW50KSkKICAKICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIGxhdGl0dWRpbmFsIGJpbgogIHdlc3RfaW5kX3NoZWxmX2FyZWFzW2ksICJsYXRpdHVkZV9zdGFydCJdIDwtIHdlc3RfaW5kX25vcnRoX2xhdGl0dWRlc1tpXQogIHdlc3RfaW5kX3NoZWxmX2FyZWFzW2ksICJsYXRpdHVkZV9lbmQiXSA8LSB3ZXN0X2luZF9ub3J0aF9sYXRpdHVkZXNbaSsxXQogIAogIGlmKGFsbChpcy5uYSh2YWx1ZXMoc2VnbWVudF9ub3J0aCkpKSkgeyAjaWYgdGhlcmUncyBubyBzaGVsZiBhcmVhIHdpdGhpbiBhIGJpbiwgYWxsIGFyZWEgPSAwCiAgICAKICB3ZXN0X2luZF9zaGVsZl9hcmVhc1tpLCAiYXJlYV9lcXVhbGFyZWFwcm9qIl0gPC0gMAogIHdlc3RfaW5kX3NoZWxmX2FyZWFzW2ksICJhcmVhX3Jhc3RlcmFyZWEiXSA8LSAwCiAgd2VzdF9pbmRfc2hlbGZfYXJlYXNbaSwgImFyZWFfcmdlb3NfZ0FyZWEiXSA8LSAwCgogIAogIHByaW50KGkpCiAgICAKICB9IGVsc2UgeyAjaWYgdGhlcmUgaXMgc2hlbGYgYXJlYSB3aXRoaW4gdGhlIGJpbiwgY2FsY3VsYXRlIGFyZWEgb2Ygc2xpY2UKICAKICAgICNyYXN0ZXIgYXJlYSBjYWxjdWxhdGlvbgogICAgICAjZ2V0IHNpemVzIG9mIGFsbCBjZWxscyBpbiByYXN0ZXIgW2ttMl0KICAgIGNlbGxfc2l6ZV9yYXN0ZXI8LWFyZWEoc2VnbWVudF9ub3J0aCwgbmEucm09VFJVRSwgd2VpZ2h0cz1GQUxTRSkKICAgIAogICAgI2RlbGV0ZSBOQXMgZnJvbSB2ZWN0b3Igb2YgYWxsIHJhc3RlciBjZWxscwogICAgY2VsbF9zaXplX3Jhc3RlcjwtY2VsbF9zaXplX3Jhc3RlclshaXMubmEoc2VnbWVudF9ub3J0aCldCiAgICAKICAgICNjb21wdXRlIGFyZWEgb2YgYWxsIGNlbGxzIGluIGdlb19yYXN0ZXIKICAgICNmdWxsIGFyZWEgICAgICAgICAgPC0gdG90YWwgIyBncmlkIGNlbGxzICAgICAqIG1lZGlhbiBjZWxsIGFyZWEgKHVzaW5nIG1lZGlhbiBjYXJlcyBsZXNzIGFib3V0IGV4dHJlbWUgdmFsdWVzKQogICAgc2VnbWVudF9hcmVhX3Jhc3RlciA8LSBsZW5ndGgoY2VsbF9zaXplX3Jhc3RlcikqbWVkaWFuKGNlbGxfc2l6ZV9yYXN0ZXIpICNpbiBrbV4yCiAgICAKICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIHJhc3RlciBhcmVhCiAgd2VzdF9pbmRfc2hlbGZfYXJlYXNbaSwgImFyZWFfcmFzdGVyYXJlYSJdIDwtIHNlZ21lbnRfYXJlYV9yYXN0ZXIKICAgIAogICNjb252ZXJ0IHRvIHNwYXRpYWwgcG9seWdvbnMgdG8gY2hlY2sgYXJlYSBjYWxjdWxhdGlvbnMKICAgIAogICNjb252ZXJ0IHNlZ21lbnQgZnJvbSByYXN0ZXIgdG8gcG9seWdvbiwgZWFjaCBjZWxsIGZyb20gdGhlIHJhc3RlciBpcyBhbiBpbmRlcGVuZGVudCBwb2x5Z29uLCAoZGlzc29sdmUgbWVhbnMgYWxsIGNlbGxzIHdpdGggYSB2YWx1ZSBvZiAxIGFyZSBhIHNpbmdsZSBwb2x5Z29uIGlmIGNvbm5lY3RlZCkKICBzZWdtZW50X25vcnRoLnNwIDwtIHJhc3RlclRvUG9seWdvbnMoc2VnbWVudF9ub3J0aCwgZGlzc29sdmUgPSBUKQogIAogICMgIElmIHggaXMgYSBTcGF0aWFsUG9seWdvbnMqIG9iamVjdDogYXJlYSBvZiBlYWNoIHNwYXRpYWwgb2JqZWN0IGluIHNxdWFyZWQgbWV0ZXJzIGlmIHRoZSBDUlMgaXMgbG9uZ2l0dWRlL2xhdGl0dWRlLCBvciBpbiBzcXVhcmVkIG1hcCB1bml0cyAodHlwaWNhbGx5IG1ldGVyKQogIAogICAgI3Byb2plY3QgdG8gZXF1YWwgZWFydGggYXJlYSBwcm9qZWN0aW9uCiAgc2VnbWVudF9ub3J0aC5zcC5FQSA8LSBzcFRyYW5zZm9ybShzZWdtZW50X25vcnRoLnNwLCBDUlNvYmogPSBlcXVhbGFyZWFwcm9qZWN0aW9uKQoKICAjY2FsY3VsYXRlIGFyZWEgb2YgdGhlIHNwYXRpYWwgb2JqZWN0IGluIG1eMgogICAgcG9seWdvbl9zaXplLnNwIDwtIGFyZWEoc2VnbWVudF9ub3J0aC5zcC5FQSkKCiAgICAjY29udmVydCBmcm9tIG1eMiB0byBrbV4yCiAgICBzZWdtZW50X2FyZWFfZXF1YWxhcmVhIDwtIHBvbHlnb25fc2l6ZS5zcC8xZTYKCiAgICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIHBvbHlnb24gYXJlYSB1c2luZyByYXN0ZXIgY2FsY3VsYXRpb24KICB3ZXN0X2luZF9zaGVsZl9hcmVhc1tpLCAiYXJlYV9lcXVhbGFyZWFwcm9qIl0gPC0gc2VnbWVudF9hcmVhX2VxdWFsYXJlYQogIAogICNhbmQgdGhlbiBwbGFpbiBhbmQgc2ltcGxlIGFsc28gdXNpbmcgcmdlb3M6OmdBcmVhCiAgCiAgYXJlYV9yZ2Vvc19nQXJlYSA8LSBnQXJlYShzZWdtZW50X25vcnRoLnNwLkVBKS8xZTYKICAKICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIHBvbHlnb24gYXJlYSB1c2luZyByZWdlb3MgY2FsY3VsYXRpb24KICB3ZXN0X2luZF9zaGVsZl9hcmVhc1tpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtICAgYXJlYV9yZ2Vvc19nQXJlYQogIAogIHByaW50KGkpCiAgfSAKfQoKI2xvb3AgZm9yIHNvdXRoCmZvciAoaSBpbiAxOihsZW5ndGgod2VzdF9pbmRfc291dGhfbGF0aXR1ZGVzKS0xKSkgewogIHNvdXRoX2V4dGVudCA8LSBjKHhtaW4od2VzdF9pbmRfc3BkZl9tYXNrXzFzKSwgeG1heCh3ZXN0X2luZF9zcGRmX21hc2tfMXMpLCB3ZXN0X2luZF9zb3V0aF9sYXRpdHVkZXNbaSsxXSwgd2VzdF9pbmRfc291dGhfbGF0aXR1ZGVzW2ldKSAjb3JkZXI9IHhtaW4sIHhtYXgsIHltaW4sIHltYXgpCiAgCiAgI3Jhc3RlciBzZWdtZW50CiAgc2VnbWVudF9zb3V0aCA8LSBjcm9wKHdlc3RfaW5kX3NwZGZfbWFza18xcywgZXh0ZW50KHNvdXRoX2V4dGVudCkpCiAgCiAgI2FkZCBsYXRpdHVkZSBiaW4gaW5mbyB0byBkYXRhIHRhYmxlCiAgICB3ZXN0X2luZF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImxhdGl0dWRlX3N0YXJ0Il0gPC0gd2VzdF9pbmRfc291dGhfbGF0aXR1ZGVzW2ldCiAgd2VzdF9pbmRfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKHdlc3RfaW5kX25vcnRoX2xhdGl0dWRlcyktMSksICJsYXRpdHVkZV9lbmQiXSA8LSB3ZXN0X2luZF9zb3V0aF9sYXRpdHVkZXNbaSsxXQogIAogIAogIGlmKGFsbChpcy5uYSh2YWx1ZXMoc2VnbWVudF9zb3V0aCkpKSkgeyAjaWYgdGhlcmUncyBubyBzaGVsZiBhcmVhIHdpdGhpbiBhIGJpbiwgbWVhbmluZyB0aGVyZSdzIG5vIHNoZWxmIGFyZWEgYXQgdGhhdCBsYXRpdHVkZQoKICB3ZXN0X2luZF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfZXF1YWxhcmVhcHJvaiJdIDwtIDAKICB3ZXN0X2luZF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfcmFzdGVyYXJlYSJdIDwtIDAKICAgIHdlc3RfaW5kX3NoZWxmX2FyZWFzW2krKGxlbmd0aCh3ZXN0X2luZF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtIDAKICAKICBwcmludChpKQogICAgCiAgfSBlbHNlIHsKICAKICAgICNyYXN0ZXIgYXJlYSBjYWxjdWxhdGlvbgogICAgICAjZ2V0IHNpemVzIG9mIGFsbCBjZWxscyBpbiByYXN0ZXIgW2ttMl0KICAgIGNlbGxfc2l6ZV9yYXN0ZXI8LWFyZWEoc2VnbWVudF9zb3V0aCwgbmEucm09VFJVRSwgd2VpZ2h0cz1GQUxTRSkKICAgIAogICAgI2RlbGV0ZSBOQXMgZnJvbSB2ZWN0b3Igb2YgYWxsIHJhc3RlciBjZWxscwogICAgY2VsbF9zaXplX3Jhc3RlcjwtY2VsbF9zaXplX3Jhc3RlclshaXMubmEoc2VnbWVudF9zb3V0aCldCiAgICAjY29tcHV0ZSBhcmVhIG9mIGFsbCBjZWxscyBpbiBnZW9fcmFzdGVyCiAgICAjZnVsbCBhcmVhICAgICAgICAgIDwtIHRvdGFsICMgZ3JpZCBjZWxscyAgICAgKiBtZWRpYW4gY2VsbCBhcmVhICh1c2luZyBtZWRpYW4gY2FyZXMgbGVzcyBhYm91dCBleHRyZW1lIHZhbHVlcykKICAgIHNlZ21lbnRfYXJlYV9yYXN0ZXIgPC0gbGVuZ3RoKGNlbGxfc2l6ZV9yYXN0ZXIpKm1lZGlhbihjZWxsX3NpemVfcmFzdGVyKQogICAgCiAgI3BvcHVsYXRlIGRhdGEgdGFibGUgd2l0aCByYXN0ZXIgYXJlYQogIHdlc3RfaW5kX3NoZWxmX2FyZWFzW2krKGxlbmd0aCh3ZXN0X2luZF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yYXN0ZXJhcmVhIl0gPC0gc2VnbWVudF9hcmVhX3Jhc3RlcgogICAgCiAgCiAgI2NvbnZlcnQgdG8gc3BhdGlhbCBwb2x5Z29ucyB0byBjaGVjayBhcmVhIGNhbGN1bGF0aW9ucwogICAgCiAgI2NvbnZlcnQgc2VnbWVudCBmcm9tIHJhc3RlciB0byBwb2x5Z29uLCBlYWNoIGNlbGwgZnJvbSB0aGUgcmFzdGVyIGlzIGFuIGluZGVwZW5kZW50IHBvbHlnb24sIChkaXNzb2x2ZSBtZWFucyBhbGwgY2VsbHMgd2l0aCBhIHZhbHVlIG9mIDEgYXJlIGEgc2luZ2xlIHBvbHlnb24gaWYgY29ubmVjdGVkKQogIHNlZ21lbnRfc291dGguc3AgPC0gcmFzdGVyVG9Qb2x5Z29ucyhzZWdtZW50X3NvdXRoLCBkaXNzb2x2ZSA9IFQpCiAgCiAgIyAgSWYgeCBpcyBhIFNwYXRpYWxQb2x5Z29ucyogb2JqZWN0OiBhcmVhIG9mIGVhY2ggc3BhdGlhbCBvYmplY3QgaW4gc3F1YXJlZCBtZXRlcnMgaWYgdGhlIENSUyBpcyBsb25naXR1ZGUvbGF0aXR1ZGUsIG9yIGluIHNxdWFyZWQgbWFwIHVuaXRzICh0eXBpY2FsbHkgbWV0ZXIpCiAgCiAgICAjcHJvamVjdCB0byBlcXVhbCBlYXJ0aCBhcmVhIHByb2plY3Rpb24KICBzZWdtZW50X3NvdXRoLnNwLkVBIDwtIHNwVHJhbnNmb3JtKHNlZ21lbnRfc291dGguc3AsIENSU29iaiA9IGVxdWFsYXJlYXByb2plY3Rpb24pCgogICNjYWxjdWxhdGUgYXJlYSBvZiB0aGUgc3BhdGlhbCBvYmplY3QgaW4gbV4yCiAgICBwb2x5Z29uX3NpemUuc3AgPC0gYXJlYShzZWdtZW50X3NvdXRoLnNwLkVBKQoKICAgICNjb252ZXJ0IGZyb20gbV4yIHRvIGttXjIKICAgIHNlZ21lbnRfYXJlYV9lcXVhbGFyZWEgPC0gcG9seWdvbl9zaXplLnNwLzFlNgoKICAgICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggYXJlYSBvZiBwb2x5Z29uIGZyb20gcmFzdGVyOjphcmVhIGZ1bmN0aW9uCiAgd2VzdF9pbmRfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKHdlc3RfaW5kX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSBzZWdtZW50X2FyZWFfZXF1YWxhcmVhCiAgCiAgI2FuZCB0aGVuIHBsYWluIGFuZCBzaW1wbGUgYWxzbyB1c2luZyByZ2Vvczo6Z0FyZWEKICAKICBhcmVhX3JnZW9zX2dBcmVhIDwtIGdBcmVhKHNlZ21lbnRfc291dGguc3AuRUEpLzFlNgogIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIGZyb20gcmdlb3MgYXJlYSBjYWxjdWxhdGlvbiBmb3IgcHJvamVjdGVkIHBvbHlnb24KICB3ZXN0X2luZF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgod2VzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfcmdlb3NfZ0FyZWEiXSA8LSAgIGFyZWFfcmdlb3NfZ0FyZWEKICAKICBwcmludChpKQogIH0gCn0KCiNjb21wYXJlIHJhc3RlcjphcmVhIGNhbGN1bGF0aW9uLCB0byBlcXVhbCBhcmVhIHN0aWxsIHVzaW5nIHJhc3Rlcjo6YXJlYSBmdW5jdGlvbiwgdG8gcmdlb3M6OmdBcmVhIGZ1bmN0aW9uIGZvciBwb2x5Z29ucwoKZ2dwbG90KGRhdGEgPSB3ZXN0X2luZF9zaGVsZl9hcmVhcykgKwogIGdlb21fcG9pbnQoYWVzKHggPSBsYXRpdHVkZV9zdGFydCwgeSA9IGFyZWFfcmdlb3NfZ0FyZWEpLCBjb2xvciA9ICJwdXJwbGUiLCBzaXplID0gMC41KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCB5ID0gYXJlYV9yYXN0ZXJhcmVhKSwgY29sb3IgPSAiZGFya2dyZWVuIiwgc2l6ZSA9IDAuNSkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBsYXRpdHVkZV9zdGFydCwgeSA9IGFyZWFfZXF1YWxhcmVhcHJvaiksIGNvbG9yID0gInJlZCIsIHNpemUgPSAwLjUpICsKICBsYWJzKHg9cGFzdGUwKCJMYXRpdHVkZSAiLCJcdTAwQjAiLCJFIiksIHkgPSAiQXJlYSBrbV4yIikgKwogIHRoZW1lX2NsYXNzaWMoKQoKY29yKHdlc3RfaW5kX3NoZWxmX2FyZWFzWywzOjVdLCB1c2UgPSAiY29tcGxldGUub2JzIikKYGBgCgpgYGB7ciBwZXJjZW50IHNoaWZ0IHdlc3Rlcm4gaW5kaWFufQp3ZXN0X2luZF9zaGVsZl9hcmVhc1ssIHBlcmNlbnRfY2hhbmdlIDo9IChhcmVhX3Jhc3RlcmFyZWEtZGF0YS50YWJsZTo6c2hpZnQoYXJlYV9yYXN0ZXJhcmVhLCB0eXBlID0gImxhZyIpKS9kYXRhLnRhYmxlOjpzaGlmdChhcmVhX3Jhc3RlcmFyZWEsIHR5cGUgPSAibGFnIildWyxhcmVhXzEwMDBzIDo9IGFyZWFfcmFzdGVyYXJlYS8xMDAwXQoKd2VzdF9pbmRfc2hlbGZfYXJlYXNbLGhlbWlzcGhlcmUgOj0gaWZlbHNlKGxhdGl0dWRlX2VuZD4wLCJub3J0aCIsInNvdXRoIildCgp3ZXN0X2luZF9zaGVsZl9hcmVhc1ssIGNoYW5nZV9hYm92ZV8yZm9sZCA6PSBpZmVsc2UoKHBlcmNlbnRfY2hhbmdlPj0xICYgcGVyY2VudF9jaGFuZ2U8SW5mKSwgMSwgaWZlbHNlKHBlcmNlbnRfY2hhbmdlPD0tMC41LCAtMSwgMCkpXQoKd2VzdF9pbmRfc2hlbGZfYXJlYXNfaGlnaGxpZ2h0IDwtIHdlc3RfaW5kX3NoZWxmX2FyZWFzW2NoYW5nZV9hYm92ZV8yZm9sZCAhPSAwLF0KCndlc3RfaW5kX3NoZWxmX2FyZWFzX3N0YXRzIDwtIHRhYmxlKHdlc3RfaW5kX3NoZWxmX2FyZWFzWywuKGNoYW5nZV9hYm92ZV8yZm9sZCwgaGVtaXNwaGVyZSldKQpgYGAKCk1vZGVsIGNoYW5nZSBmb3Igc291dGhlcm4gYW5kIG5vcnRoZXJuIGhlbWlzcGhlcmUKYGBge3IgbW9kZWwgbm9ydGhlcm4gdmVyc3VzIHNvdXRoZXJuIHdlc3Rlcm4gaW5kaWFufQojYWRkIG1pZCBsYXRpdHVkZQp3ZXN0X2luZF9zaGVsZl9hcmVhc1ssbGF0aXR1ZGVfbWlkIDo9IGFicygobGF0aXR1ZGVfZW5kK2xhdGl0dWRlX3N0YXJ0KS8yKV0KCndlc3RfaW5kX25vcnRoX21vZCA8LSBsbShkYXRhID0gd2VzdF9pbmRfc2hlbGZfYXJlYXNbaGVtaXNwaGVyZSA9PSAibm9ydGgiXSwgYXJlYV9yYXN0ZXJhcmVhIH4gbGF0aXR1ZGVfbWlkKQpzdW1tYXJ5KHdlc3RfaW5kX25vcnRoX21vZCkKCndlc3RfaW5kX3NvdXRoX21vZCA8LSBsbShkYXRhID0gd2VzdF9pbmRfc2hlbGZfYXJlYXNbaGVtaXNwaGVyZSA9PSAic291dGgiXSwgYXJlYV9yYXN0ZXJhcmVhIH4gbGF0aXR1ZGVfbWlkKQpzdW1tYXJ5KHdlc3RfaW5kX3NvdXRoX21vZCkKYGBgCgpFYXN0ZXJuIEluZGlhbgoKZWFzdF9pbmRfc3BkZl9tYXNrXzFzCgpgYGB7ciBzcGxpdCByYXN0ZXIgZm9yIGVhc3Rlcm4gaW5kaWFufQpub3J0aF9leHRlbnQgPC0gYyh4bWluKGVhc3RfaW5kX3NwZGZfbWFza18xcyksIHhtYXgoZWFzdF9pbmRfc3BkZl9tYXNrXzFzKSwgMCwgeW1heChlYXN0X2luZF9zcGRmX21hc2tfMXMpKQpzb3V0aF9leHRlbnQgPC0gYyh4bWluKGVhc3RfaW5kX3NwZGZfbWFza18xcyksIHhtYXgoZWFzdF9pbmRfc3BkZl9tYXNrXzFzKSwgeW1pbihlYXN0X2luZF9zcGRmX21hc2tfMXMpLCAwKQoKI2Nyb3AgZWFzdF9pbmQgcmFzdGVyIGFib3ZlIGFuZCBiZWxvdyAwCmVhc3RfaW5kX3NwZGZfc2hpZnRfYWdnX25vcnRoIDwtIGNyb3AoZWFzdF9pbmRfc3BkZl9tYXNrXzFzLCBleHRlbnQobm9ydGhfZXh0ZW50KSkKCmVhc3RfaW5kX3NwZGZfc2hpZnRfYWdnX3NvdXRoIDwtIGNyb3AoZWFzdF9pbmRfc3BkZl9tYXNrXzFzLCBleHRlbnQoc291dGhfZXh0ZW50KSkKCiN1bmZvcnR1bmF0ZWx5LCBJIHRoaW5rIEkgbWF5IGhhdmUgdG8ganVzdCBkbyB0aGlzIG1hbnVhbGx5ICh1Z2x5LCBJIGtub3cpCgojYWxsIGNodW5rcyBmb3IgZWFzdCBpbmRpYW4KZWFzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzIDwtIHNlcSgwLCB5bWF4KGVhc3RfaW5kX3NwZGZfbWFza18xcyksIGJ5ID0gMikKZWFzdF9pbmRfc291dGhfbGF0aXR1ZGVzIDwtIHNlcSgwLCB5bWluKGVhc3RfaW5kX3NwZGZfbWFza18xcyksIGJ5ID0gLTIpCgojc2V0dXAgZGF0YSB0YWJsZSB0byBwb3B1bGF0ZSBpbiBsb29wLCBzdWJ0cmFjdGluZyBvbmUgdG8gYWxsb3cgZm9yIGJpbnMKZWFzdF9pbmRfc2hlbGZfYXJlYXMgPC0gYXMuZGF0YS50YWJsZShtYXRyaXgobnJvdyA9IChsZW5ndGgoZWFzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzKS0xK2xlbmd0aChlYXN0X2luZF9zb3V0aF9sYXRpdHVkZXMpLTEpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKZWFzdF9pbmRfc2hlbGZfYXJlYXNbLCBsYXRpdHVkZV9zdGFydCA6PSBhcy5udW1lcmljKFYxKV1bLCBsYXRpdHVkZV9lbmQgOj0gYXMubnVtZXJpYyhWMSldWywgYXJlYV9yYXN0ZXJhcmVhIDo9IGFzLm51bWVyaWMoVjEpXVssIGFyZWFfZXF1YWxhcmVhcHJvaiA6PSBhcy5udW1lcmljKFYxKV1bLCBhcmVhX3JnZW9zX2dBcmVhIDo9IGFzLm51bWVyaWMoVjEpXVssIFYxIDo9IE5VTExdCgojbG9vcCBmb3Igbm9ydGgKZm9yIChpIGluIDE6KGxlbmd0aChlYXN0X2luZF9ub3J0aF9sYXRpdHVkZXMpLTEpKSB7CiAgI3NldHRpbmcgdXAgZXh0ZW50IGZvciBzbGljaW5nIGJ5IG1pbiBhbmQgbWF4IGxvbmdpdHVkZXMsIGFuZCBpIHRvIGkrMSBsYXRpdHVkZXMKICBub3J0aF9leHRlbnQgPC0gYyh4bWluKGVhc3RfaW5kX3NwZGZfbWFza18xcyksIHhtYXgoZWFzdF9pbmRfc3BkZl9tYXNrXzFzKSwgZWFzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzW2ldLCBlYXN0X2luZF9ub3J0aF9sYXRpdHVkZXNbaSsxXSkKICAKICAjY3JvcCByYXN0ZXIgc2VnZW1lbnQgYmFzZWQgb24gYmluIGV4dGVudAogIHNlZ21lbnRfbm9ydGggPC0gY3JvcChlYXN0X2luZF9zcGRmX21hc2tfMXMsIGV4dGVudChub3J0aF9leHRlbnQpKQogIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggbGF0aXR1ZGluYWwgYmluCiAgZWFzdF9pbmRfc2hlbGZfYXJlYXNbaSwgImxhdGl0dWRlX3N0YXJ0Il0gPC0gZWFzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzW2ldCiAgZWFzdF9pbmRfc2hlbGZfYXJlYXNbaSwgImxhdGl0dWRlX2VuZCJdIDwtIGVhc3RfaW5kX25vcnRoX2xhdGl0dWRlc1tpKzFdCiAgCiAgaWYoYWxsKGlzLm5hKHZhbHVlcyhzZWdtZW50X25vcnRoKSkpKSB7ICNpZiB0aGVyZSdzIG5vIHNoZWxmIGFyZWEgd2l0aGluIGEgYmluLCBhbGwgYXJlYSA9IDAKICAgIAogIGVhc3RfaW5kX3NoZWxmX2FyZWFzW2ksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSAwCiAgZWFzdF9pbmRfc2hlbGZfYXJlYXNbaSwgImFyZWFfcmFzdGVyYXJlYSJdIDwtIDAKICBlYXN0X2luZF9zaGVsZl9hcmVhc1tpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtIDAKCiAgCiAgcHJpbnQoaSkKICAgIAogIH0gZWxzZSB7ICNpZiB0aGVyZSBpcyBzaGVsZiBhcmVhIHdpdGhpbiB0aGUgYmluLCBjYWxjdWxhdGUgYXJlYSBvZiBzbGljZQogIAogICAgI3Jhc3RlciBhcmVhIGNhbGN1bGF0aW9uCiAgICAgICNnZXQgc2l6ZXMgb2YgYWxsIGNlbGxzIGluIHJhc3RlciBba20yXQogICAgY2VsbF9zaXplX3Jhc3RlcjwtYXJlYShzZWdtZW50X25vcnRoLCBuYS5ybT1UUlVFLCB3ZWlnaHRzPUZBTFNFKQogICAgCiAgICAjZGVsZXRlIE5BcyBmcm9tIHZlY3RvciBvZiBhbGwgcmFzdGVyIGNlbGxzCiAgICBjZWxsX3NpemVfcmFzdGVyPC1jZWxsX3NpemVfcmFzdGVyWyFpcy5uYShzZWdtZW50X25vcnRoKV0KICAgIAogICAgI2NvbXB1dGUgYXJlYSBvZiBhbGwgY2VsbHMgaW4gZ2VvX3Jhc3RlcgogICAgI2Z1bGwgYXJlYSAgICAgICAgICA8LSB0b3RhbCAjIGdyaWQgY2VsbHMgICAgICogbWVkaWFuIGNlbGwgYXJlYSAodXNpbmcgbWVkaWFuIGNhcmVzIGxlc3MgYWJvdXQgZXh0cmVtZSB2YWx1ZXMpCiAgICBzZWdtZW50X2FyZWFfcmFzdGVyIDwtIGxlbmd0aChjZWxsX3NpemVfcmFzdGVyKSptZWRpYW4oY2VsbF9zaXplX3Jhc3RlcikgI2luIGttXjIKICAgIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcmFzdGVyIGFyZWEKICBlYXN0X2luZF9zaGVsZl9hcmVhc1tpLCAiYXJlYV9yYXN0ZXJhcmVhIl0gPC0gc2VnbWVudF9hcmVhX3Jhc3RlcgogICAgCiAgI2NvbnZlcnQgdG8gc3BhdGlhbCBwb2x5Z29ucyB0byBjaGVjayBhcmVhIGNhbGN1bGF0aW9ucwogICAgCiAgI2NvbnZlcnQgc2VnbWVudCBmcm9tIHJhc3RlciB0byBwb2x5Z29uLCBlYWNoIGNlbGwgZnJvbSB0aGUgcmFzdGVyIGlzIGFuIGluZGVwZW5kZW50IHBvbHlnb24sIChkaXNzb2x2ZSBtZWFucyBhbGwgY2VsbHMgd2l0aCBhIHZhbHVlIG9mIDEgYXJlIGEgc2luZ2xlIHBvbHlnb24gaWYgY29ubmVjdGVkKQogIHNlZ21lbnRfbm9ydGguc3AgPC0gcmFzdGVyVG9Qb2x5Z29ucyhzZWdtZW50X25vcnRoLCBkaXNzb2x2ZSA9IFQpCiAgCiAgIyAgSWYgeCBpcyBhIFNwYXRpYWxQb2x5Z29ucyogb2JqZWN0OiBhcmVhIG9mIGVhY2ggc3BhdGlhbCBvYmplY3QgaW4gc3F1YXJlZCBtZXRlcnMgaWYgdGhlIENSUyBpcyBsb25naXR1ZGUvbGF0aXR1ZGUsIG9yIGluIHNxdWFyZWQgbWFwIHVuaXRzICh0eXBpY2FsbHkgbWV0ZXIpCiAgCiAgICAjcHJvamVjdCB0byBlcXVhbCBlYXJ0aCBhcmVhIHByb2plY3Rpb24KICBzZWdtZW50X25vcnRoLnNwLkVBIDwtIHNwVHJhbnNmb3JtKHNlZ21lbnRfbm9ydGguc3AsIENSU29iaiA9IGVxdWFsYXJlYXByb2plY3Rpb24pCgogICNjYWxjdWxhdGUgYXJlYSBvZiB0aGUgc3BhdGlhbCBvYmplY3QgaW4gbV4yCiAgICBwb2x5Z29uX3NpemUuc3AgPC0gYXJlYShzZWdtZW50X25vcnRoLnNwLkVBKQoKICAgICNjb252ZXJ0IGZyb20gbV4yIHRvIGttXjIKICAgIHNlZ21lbnRfYXJlYV9lcXVhbGFyZWEgPC0gcG9seWdvbl9zaXplLnNwLzFlNgoKICAgICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcG9seWdvbiBhcmVhIHVzaW5nIHJhc3RlciBjYWxjdWxhdGlvbgogIGVhc3RfaW5kX3NoZWxmX2FyZWFzW2ksICJhcmVhX2VxdWFsYXJlYXByb2oiXSA8LSBzZWdtZW50X2FyZWFfZXF1YWxhcmVhCiAgCiAgI2FuZCB0aGVuIHBsYWluIGFuZCBzaW1wbGUgYWxzbyB1c2luZyByZ2Vvczo6Z0FyZWEKICAKICBhcmVhX3JnZW9zX2dBcmVhIDwtIGdBcmVhKHNlZ21lbnRfbm9ydGguc3AuRUEpLzFlNgogIAogICNwb3B1bGF0ZSBkYXRhIHRhYmxlIHdpdGggcG9seWdvbiBhcmVhIHVzaW5nIHJlZ2VvcyBjYWxjdWxhdGlvbgogIGVhc3RfaW5kX3NoZWxmX2FyZWFzW2ksICJhcmVhX3JnZW9zX2dBcmVhIl0gPC0gICBhcmVhX3JnZW9zX2dBcmVhCiAgCiAgcHJpbnQoaSkKICB9IAp9CgojbG9vcCBmb3Igc291dGgKZm9yIChpIGluIDE6KGxlbmd0aChlYXN0X2luZF9zb3V0aF9sYXRpdHVkZXMpLTEpKSB7CiAgc291dGhfZXh0ZW50IDwtIGMoeG1pbihlYXN0X2luZF9zcGRmX21hc2tfMXMpLCB4bWF4KGVhc3RfaW5kX3NwZGZfbWFza18xcyksIGVhc3RfaW5kX3NvdXRoX2xhdGl0dWRlc1tpKzFdLCBlYXN0X2luZF9zb3V0aF9sYXRpdHVkZXNbaV0pICNvcmRlcj0geG1pbiwgeG1heCwgeW1pbiwgeW1heCkKICAKICAjcmFzdGVyIHNlZ21lbnQKICBzZWdtZW50X3NvdXRoIDwtIGNyb3AoZWFzdF9pbmRfc3BkZl9tYXNrXzFzLCBleHRlbnQoc291dGhfZXh0ZW50KSkKICAKICAjYWRkIGxhdGl0dWRlIGJpbiBpbmZvIHRvIGRhdGEgdGFibGUKICAgIGVhc3RfaW5kX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X2luZF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAibGF0aXR1ZGVfc3RhcnQiXSA8LSBlYXN0X2luZF9zb3V0aF9sYXRpdHVkZXNbaV0KICBlYXN0X2luZF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgoZWFzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImxhdGl0dWRlX2VuZCJdIDwtIGVhc3RfaW5kX3NvdXRoX2xhdGl0dWRlc1tpKzFdCiAgCiAgCiAgaWYoYWxsKGlzLm5hKHZhbHVlcyhzZWdtZW50X3NvdXRoKSkpKSB7ICNpZiB0aGVyZSdzIG5vIHNoZWxmIGFyZWEgd2l0aGluIGEgYmluLCBtZWFuaW5nIHRoZXJlJ3Mgbm8gc2hlbGYgYXJlYSBhdCB0aGF0IGxhdGl0dWRlCgogIGVhc3RfaW5kX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X2luZF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9lcXVhbGFyZWFwcm9qIl0gPC0gMAogIGVhc3RfaW5kX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X2luZF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yYXN0ZXJhcmVhIl0gPC0gMAogICAgZWFzdF9pbmRfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKGVhc3RfaW5kX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX3JnZW9zX2dBcmVhIl0gPC0gMAogIAogIHByaW50KGkpCiAgICAKICB9IGVsc2UgewogIAogICAgI3Jhc3RlciBhcmVhIGNhbGN1bGF0aW9uCiAgICAgICNnZXQgc2l6ZXMgb2YgYWxsIGNlbGxzIGluIHJhc3RlciBba20yXQogICAgY2VsbF9zaXplX3Jhc3RlcjwtYXJlYShzZWdtZW50X3NvdXRoLCBuYS5ybT1UUlVFLCB3ZWlnaHRzPUZBTFNFKQogICAgCiAgICAjZGVsZXRlIE5BcyBmcm9tIHZlY3RvciBvZiBhbGwgcmFzdGVyIGNlbGxzCiAgICBjZWxsX3NpemVfcmFzdGVyPC1jZWxsX3NpemVfcmFzdGVyWyFpcy5uYShzZWdtZW50X3NvdXRoKV0KICAgICNjb21wdXRlIGFyZWEgb2YgYWxsIGNlbGxzIGluIGdlb19yYXN0ZXIKICAgICNmdWxsIGFyZWEgICAgICAgICAgPC0gdG90YWwgIyBncmlkIGNlbGxzICAgICAqIG1lZGlhbiBjZWxsIGFyZWEgKHVzaW5nIG1lZGlhbiBjYXJlcyBsZXNzIGFib3V0IGV4dHJlbWUgdmFsdWVzKQogICAgc2VnbWVudF9hcmVhX3Jhc3RlciA8LSBsZW5ndGgoY2VsbF9zaXplX3Jhc3RlcikqbWVkaWFuKGNlbGxfc2l6ZV9yYXN0ZXIpCiAgICAKICAjcG9wdWxhdGUgZGF0YSB0YWJsZSB3aXRoIHJhc3RlciBhcmVhCiAgZWFzdF9pbmRfc2hlbGZfYXJlYXNbaSsobGVuZ3RoKGVhc3RfaW5kX25vcnRoX2xhdGl0dWRlcyktMSksICJhcmVhX3Jhc3RlcmFyZWEiXSA8LSBzZWdtZW50X2FyZWFfcmFzdGVyCiAgICAKICAKICAjY29udmVydCB0byBzcGF0aWFsIHBvbHlnb25zIHRvIGNoZWNrIGFyZWEgY2FsY3VsYXRpb25zCiAgICAKICAjY29udmVydCBzZWdtZW50IGZyb20gcmFzdGVyIHRvIHBvbHlnb24sIGVhY2ggY2VsbCBmcm9tIHRoZSByYXN0ZXIgaXMgYW4gaW5kZXBlbmRlbnQgcG9seWdvbiwgKGRpc3NvbHZlIG1lYW5zIGFsbCBjZWxscyB3aXRoIGEgdmFsdWUgb2YgMSBhcmUgYSBzaW5nbGUgcG9seWdvbiBpZiBjb25uZWN0ZWQpCiAgc2VnbWVudF9zb3V0aC5zcCA8LSByYXN0ZXJUb1BvbHlnb25zKHNlZ21lbnRfc291dGgsIGRpc3NvbHZlID0gVCkKICAKICAjICBJZiB4IGlzIGEgU3BhdGlhbFBvbHlnb25zKiBvYmplY3Q6IGFyZWEgb2YgZWFjaCBzcGF0aWFsIG9iamVjdCBpbiBzcXVhcmVkIG1ldGVycyBpZiB0aGUgQ1JTIGlzIGxvbmdpdHVkZS9sYXRpdHVkZSwgb3IgaW4gc3F1YXJlZCBtYXAgdW5pdHMgKHR5cGljYWxseSBtZXRlcikKICAKICAgICNwcm9qZWN0IHRvIGVxdWFsIGVhcnRoIGFyZWEgcHJvamVjdGlvbgogIHNlZ21lbnRfc291dGguc3AuRUEgPC0gc3BUcmFuc2Zvcm0oc2VnbWVudF9zb3V0aC5zcCwgQ1JTb2JqID0gZXF1YWxhcmVhcHJvamVjdGlvbikKCiAgI2NhbGN1bGF0ZSBhcmVhIG9mIHRoZSBzcGF0aWFsIG9iamVjdCBpbiBtXjIKICAgIHBvbHlnb25fc2l6ZS5zcCA8LSBhcmVhKHNlZ21lbnRfc291dGguc3AuRUEpCgogICAgI2NvbnZlcnQgZnJvbSBtXjIgdG8ga21eMgogICAgc2VnbWVudF9hcmVhX2VxdWFsYXJlYSA8LSBwb2x5Z29uX3NpemUuc3AvMWU2CgogICAgI3BvcHVsYXRlIGRhdGEgdGFibGUgd2l0aCBhcmVhIG9mIHBvbHlnb24gZnJvbSByYXN0ZXI6OmFyZWEgZnVuY3Rpb24KICBlYXN0X2luZF9zaGVsZl9hcmVhc1tpKyhsZW5ndGgoZWFzdF9pbmRfbm9ydGhfbGF0aXR1ZGVzKS0xKSwgImFyZWFfZXF1YWxhcmVhcHJvaiJdIDwtIHNlZ21lbnRfYXJlYV9lcXVhbGFyZWEKICAKICAjYW5kIHRoZW4gcGxhaW4gYW5kIHNpbXBsZSBhbHNvIHVzaW5nIHJnZW9zOjpnQXJlYQogIAogIGFyZWFfcmdlb3NfZ0FyZWEgPC0gZ0FyZWEoc2VnbWVudF9zb3V0aC5zcC5FQSkvMWU2CiAgCiAgI3BvcHVsYXRlIGRhdGEgdGFibGUgZnJvbSByZ2VvcyBhcmVhIGNhbGN1bGF0aW9uIGZvciBwcm9qZWN0ZWQgcG9seWdvbgogIGVhc3RfaW5kX3NoZWxmX2FyZWFzW2krKGxlbmd0aChlYXN0X2luZF9ub3J0aF9sYXRpdHVkZXMpLTEpLCAiYXJlYV9yZ2Vvc19nQXJlYSJdIDwtICAgYXJlYV9yZ2Vvc19nQXJlYQogIAogIHByaW50KGkpCiAgfSAKfQoKI2NvbXBhcmUgcmFzdGVyOmFyZWEgY2FsY3VsYXRpb24sIHRvIGVxdWFsIGFyZWEgc3RpbGwgdXNpbmcgcmFzdGVyOjphcmVhIGZ1bmN0aW9uLCB0byByZ2Vvczo6Z0FyZWEgZnVuY3Rpb24gZm9yIHBvbHlnb25zCgpnZ3Bsb3QoZGF0YSA9IGVhc3RfaW5kX3NoZWxmX2FyZWFzKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCB5ID0gYXJlYV9yZ2Vvc19nQXJlYSksIGNvbG9yID0gInB1cnBsZSIsIHNpemUgPSAwLjUpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gbGF0aXR1ZGVfc3RhcnQsIHkgPSBhcmVhX3Jhc3RlcmFyZWEpLCBjb2xvciA9ICJkYXJrZ3JlZW4iLCBzaXplID0gMC41KSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCB5ID0gYXJlYV9lcXVhbGFyZWFwcm9qKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDAuNSkgKwogIGxhYnMoeD1wYXN0ZTAoIkxhdGl0dWRlICIsIlx1MDBCMCIsIkUiKSwgeSA9ICJBcmVhIGttXjIiKSArCiAgdGhlbWVfY2xhc3NpYygpCgpjb3IoZWFzdF9pbmRfc2hlbGZfYXJlYXNbLDM6NV0sIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQoKYGBgCgpgYGB7ciBwZXJjZW50IHNoaWZ0IGVhc3Rlcm4gaW5kaWFufQplYXN0X2luZF9zaGVsZl9hcmVhc1ssIHBlcmNlbnRfY2hhbmdlIDo9IChhcmVhX3Jhc3RlcmFyZWEtZGF0YS50YWJsZTo6c2hpZnQoYXJlYV9yYXN0ZXJhcmVhLCB0eXBlID0gImxhZyIpKS9kYXRhLnRhYmxlOjpzaGlmdChhcmVhX3Jhc3RlcmFyZWEsIHR5cGUgPSAibGFnIildWyxhcmVhXzEwMDBzIDo9IGFyZWFfcmFzdGVyYXJlYS8xMDAwXQoKZWFzdF9pbmRfc2hlbGZfYXJlYXNbLGhlbWlzcGhlcmUgOj0gaWZlbHNlKGxhdGl0dWRlX2VuZD4wLCJub3J0aCIsInNvdXRoIildCgplYXN0X2luZF9zaGVsZl9hcmVhc1ssIGNoYW5nZV9hYm92ZV8yZm9sZCA6PSBpZmVsc2UoKHBlcmNlbnRfY2hhbmdlPj0xICYgcGVyY2VudF9jaGFuZ2U8SW5mKSwgMSwgaWZlbHNlKHBlcmNlbnRfY2hhbmdlPD0tMC41LCAtMSwgMCkpXQoKZWFzdF9pbmRfc2hlbGZfYXJlYXNfaGlnaGxpZ2h0IDwtIGVhc3RfaW5kX3NoZWxmX2FyZWFzW2NoYW5nZV9hYm92ZV8yZm9sZCAhPSAwLF0KCmVhc3RfaW5kX3NoZWxmX2FyZWFzX3N0YXRzIDwtIHRhYmxlKGVhc3RfaW5kX3NoZWxmX2FyZWFzWywuKGNoYW5nZV9hYm92ZV8yZm9sZCxoZW1pc3BoZXJlKV0pCmBgYAoKTW9kZWwgY2hhbmdlIGZvciBzb3V0aGVybiBhbmQgbm9ydGhlcm4gaGVtaXNwaGVyZQpgYGB7ciBtb2RlbCBub3J0aGVybiB2ZXJzdXMgc291dGhlcm4gZWFzdGVybiBpbmRpYW59CiNhZGQgbWlkIGxhdGl0dWRlCmVhc3RfaW5kX3NoZWxmX2FyZWFzWyxsYXRpdHVkZV9taWQgOj0gYWJzKChsYXRpdHVkZV9lbmQrbGF0aXR1ZGVfc3RhcnQpLzIpXQoKZWFzdF9pbmRfbm9ydGhfbW9kIDwtIGxtKGRhdGEgPSBlYXN0X2luZF9zaGVsZl9hcmVhc1toZW1pc3BoZXJlID09ICJub3J0aCJdLCBhcmVhX3Jhc3RlcmFyZWEgfiBsYXRpdHVkZV9taWQpCnN1bW1hcnkoZWFzdF9pbmRfbm9ydGhfbW9kKQoKZWFzdF9pbmRfc291dGhfbW9kIDwtIGxtKGRhdGEgPSBlYXN0X2luZF9zaGVsZl9hcmVhc1toZW1pc3BoZXJlID09ICJzb3V0aCJdLCBhcmVhX3Jhc3RlcmFyZWEgfiBsYXRpdHVkZV9taWQpCnN1bW1hcnkoZWFzdF9pbmRfc291dGhfbW9kKQpgYGAKCgpQbG90cyBvZiBsYXRpdHVkZSB2ZXJzdXMgaGFiaXRhdCBhdmFpbGFiaWxpdHkKCkluY2x1ZGUgdHJlbmQgbGluZXMgb25seSBmb3IgdGhvc2Ugd2l0aCBzaWduaWZpY2FudCBjb2VmZmljaWVudHMgYmV0d2VlbiBsYXRpdHVkZSBzdGFydCBhbmQgYXJlYV4yCk5PVDoKLSBFYXN0IEluZGlhbiBTb3V0aGVybiBhbmQgTm9ydGhlcm4KLSBFYXN0IFBhY2lmaWMgU291dGhlcm4KLSBXZXN0IEluZGlhbiBTb3V0aGVybgoKYGBge3IgcGxvdHMgbGF0aXR1ZGUgaGFiaXRhdCBhdmFpbGFiaWxpdHl9CiMjZWFzdCBpbmRpYW4KI3NpbXVsYXRlIGRhdGEgdG8gcGxvdCB0cmVuZGxpbmUKZWFzdF9pbmRfbm9ydGhfZGF0YSA8LSBkYXRhLnRhYmxlKGxhdGl0dWRlX21pZCA9IHVuaXF1ZShlYXN0X2luZF9zaGVsZl9hcmVhc1tsYXRpdHVkZV9lbmQ+MCxdJGxhdGl0dWRlX21pZCkpCgplYXN0X2luZF9ub3J0aF9kYXRhWyxhcmVhXzEwMDBzIDo9IGVhc3RfaW5kX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzFdXS8xMDAwK2Vhc3RfaW5kX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzJdXS8xMDAwKmxhdGl0dWRlX21pZF0KCmVhc3RfaW5kX3NvdXRoX2RhdGEgPC0gZGF0YS50YWJsZShsYXRpdHVkZV9taWQgPSB1bmlxdWUoZWFzdF9pbmRfc2hlbGZfYXJlYXNbbGF0aXR1ZGVfZW5kIDwwLF0kbGF0aXR1ZGVfbWlkKSkKCmVhc3RfaW5kX3NvdXRoX2RhdGFbLGFyZWFfMTAwMHMgOj0gZWFzdF9pbmRfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMV1dLzEwMDArZWFzdF9pbmRfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMl1dLzEwMDAqbGF0aXR1ZGVfbWlkXQoKI2Vhc3QgaW5kaWFuCihhcmVhX2xhdGl0dWRlX2Vhc3RfaW5kICA8LSBnZ3Bsb3QoKSArCiMgIGdlb21fbGluZShkYXRhID0gZWFzdF9pbmRfbm9ydGhfZGF0YSwgYWVzKHg9bGF0aXR1ZGVfbWlkLCB5ID0gYXJlYV8xMDAwcyksIGNvbG9yID0gImdyYXk2MCIsIHNpemUgPSAwLjgsIGxpbmV0eXBlID0gImxvbmdkYXNoIikgKyBub3Qgc2lnbmlmaWNhbnQKIyAgZ2VvbV9saW5lKGRhdGEgPSBlYXN0X2luZF9zb3V0aF9kYXRhLCBhZXMoeD1sYXRpdHVkZV9taWQsIHkgPSBhcmVhXzEwMDBzKSwgY29sb3IgPSAiZ3JheTYwIiwgc2l6ZSA9IDAuOCwgbGluZXR5cGUgPSAibG9uZ2Rhc2giKSArIG5vdCBzaWduaWZpY2FudAogIGdlb21fcG9pbnQoZGF0YSA9IGVhc3RfaW5kX3NoZWxmX2FyZWFzLCBhZXMoeD1sYXRpdHVkZV9zdGFydCwgeT1hcmVhXzEwMDBzKSwgc2hhcGUgPTE4LCBzaXplID0gMC43KSArCiAgZ2VvbV9saW5lKGRhdGEgPSBlYXN0X2luZF9zaGVsZl9hcmVhcywgYWVzKHg9bGF0aXR1ZGVfc3RhcnQsIHk9YXJlYV8xMDAwcyksIHNpemUgPSAwLjcpICsKICBnZW9tX3J1ZyhkYXRhID0gZWFzdF9pbmRfc2hlbGZfYXJlYXNfaGlnaGxpZ2h0LCBhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCBjb2xvciA9IGFzLmZhY3RvcihjaGFuZ2VfYWJvdmVfMmZvbGQpKSkgKwogbGFicyh4PXBhc3RlMCgiTGF0aXR1ZGUgIiwiXHUwMEIwIiwiRSIpLCB5ID0gZXhwcmVzc2lvbihwYXN0ZSgiQXJlYSAoMTAwMHMgb2YgIiwga21eezJ9LCIpIikpKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG9wdGlvbiA9ICJBIiwgYmVnaW4gPSAwLjMsIGVuZCA9IDAuNywgbmFtZSA9ICJBc3NvY2lhdGVkIENoYW5nZVxuaW4gU2hlbGYgQXJlYSIsIGxhYmVscyA9IGMoIkNvbnRyYWN0aW9uIiwgIkV4cGFuc2lvbiIpKSArCiAgIyNhbm5vdGF0ZSgidGV4dCIsIHggPTIyLCB5ID0gNzAwMDAsIGxhYmVsID0gIkVhc3Rlcm4gSW5kaWFuIE9jZWFuIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDApICsKICB4bGltKG1pbihlYXN0X2luZF9zaGVsZl9hcmVhcyRsYXRpdHVkZV9lbmQpLCBtYXgoZWFzdF9pbmRfc2hlbGZfYXJlYXMkbGF0aXR1ZGVfZW5kKSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbigxMCwgNDAsIDEwLCAxMCkpKQoKCiAgZ2dzYXZlKGFyZWFfbGF0aXR1ZGVfZWFzdF9pbmQsIGZpbGVuYW1lID0gImFyZWFfbGF0aXR1ZGVfZWFzdF9pbmRfMmRlZ3JlZXMuanBnIiwgaGVpZ2h0ID0gNCwgdW5pdHMgPSBjKCJpbiIpKQogIAojI3dlc3QgaW5kaWFuCndlc3RfaW5kX25vcnRoX2RhdGEgPC0gZGF0YS50YWJsZShsYXRpdHVkZV9taWQgPSB1bmlxdWUod2VzdF9pbmRfc2hlbGZfYXJlYXNbbGF0aXR1ZGVfZW5kPjAsXSRsYXRpdHVkZV9taWQpKQoKd2VzdF9pbmRfbm9ydGhfZGF0YVssYXJlYV8xMDAwcyA6PSB3ZXN0X2luZF9ub3J0aF9tb2QkY29lZmZpY2llbnRzW1sxXV0vMTAwMCt3ZXN0X2luZF9ub3J0aF9tb2QkY29lZmZpY2llbnRzW1syXV0vMTAwMCpsYXRpdHVkZV9taWRdCgp3ZXN0X2luZF9zb3V0aF9kYXRhIDwtIGRhdGEudGFibGUobGF0aXR1ZGVfbWlkID0gdW5pcXVlKHdlc3RfaW5kX3NoZWxmX2FyZWFzW2xhdGl0dWRlX2VuZCA8MCxdJGxhdGl0dWRlX21pZCkpCgp3ZXN0X2luZF9zb3V0aF9kYXRhWyxhcmVhXzEwMDBzIDo9IHdlc3RfaW5kX3NvdXRoX21vZCRjb2VmZmljaWVudHNbWzFdXS8xMDAwK3dlc3RfaW5kX3NvdXRoX21vZCRjb2VmZmljaWVudHNbWzJdXS8xMDAwKmxhdGl0dWRlX21pZF0KCihhcmVhX2xhdGl0dWRlX3dlc3RfaW5kICA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSB3ZXN0X2luZF9ub3J0aF9kYXRhLCBhZXMoeD1sYXRpdHVkZV9taWQsIHkgPSBhcmVhXzEwMDBzKSwgY29sb3IgPSAiZ3JheTYwIiwgc2l6ZSA9IDAuOCwgbGluZXR5cGUgPSAibG9uZ2Rhc2giKSArCiAjIGdlb21fbGluZShkYXRhID0gd2VzdF9pbmRfc291dGhfZGF0YSwgYWVzKHg9bGF0aXR1ZGVfbWlkLCB5ID0gYXJlYV8xMDAwcyksIGNvbG9yID0gImdyYXk2MCIsIHNpemUgPSAwLjgsIGxpbmV0eXBlID0gImxvbmdkYXNoIikgKyBub3Qgc2lnbmlmaWNhbnQKICBnZW9tX3BvaW50KGRhdGEgPSB3ZXN0X2luZF9zaGVsZl9hcmVhcywgYWVzKHg9bGF0aXR1ZGVfc3RhcnQsIHk9YXJlYV8xMDAwcyksIHNoYXBlID0xOCwgc2l6ZSA9IDAuNykgKyAKICBnZW9tX2xpbmUoZGF0YSA9IHdlc3RfaW5kX3NoZWxmX2FyZWFzLCBhZXMoeD1sYXRpdHVkZV9zdGFydCwgeT1hcmVhXzEwMDBzKSwgc2l6ZSA9IDAuNykgKwogICAgZ2VvbV9ydWcoZGF0YSA9IHdlc3RfaW5kX3NoZWxmX2FyZWFzX2hpZ2hsaWdodCwgYWVzKHggPSBsYXRpdHVkZV9zdGFydCwgY29sb3IgPSBhcy5mYWN0b3IoY2hhbmdlX2Fib3ZlXzJmb2xkKSkpICsKIGxhYnMoeD1wYXN0ZTAoIkxhdGl0dWRlICIsIlx1MDBCMCIsIkUiKSwgeSA9IGV4cHJlc3Npb24ocGFzdGUoIkFyZWEgKDEwMDBzIG9mICIsIGttXnsyfSwiKSIpKSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChvcHRpb24gPSAiQSIsIGJlZ2luID0gMC4zLCBlbmQgPSAwLjcsIG5hbWUgPSAiQXNzb2NpYXRlZCBDaGFuZ2VcbmluIFNoZWxmIEFyZWEiLCBsYWJlbHMgPSBjKCJDb250cmFjdGlvbiIsICJFeHBhbnNpb24iKSkgKwogICNhbm5vdGF0ZSgidGV4dCIsIHggPSAzMCwgeSA9IDMzMDAwLCBsYWJlbCA9ICJXZXN0ZXJuIEluZGlhbiBPY2VhbiIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwKSArCiAgeGxpbShtaW4od2VzdF9pbmRfc2hlbGZfYXJlYXMkbGF0aXR1ZGVfZW5kKSwgbWF4KHdlc3RfaW5kX3NoZWxmX2FyZWFzJGxhdGl0dWRlX2VuZCkpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSBtYXJnaW4oMTAsIDQwLCAxMCwgMTApKSkKCiAgZ2dzYXZlKGFyZWFfbGF0aXR1ZGVfd2VzdF9pbmQsIGZpbGVuYW1lID0gImFyZWFfbGF0aXR1ZGVfd2VzdF9pbmRfMmRlZ3JlZXMuanBnIiwgaGVpZ2h0ID0gNCwgdW5pdHMgPSBjKCJpbiIpKQoKd2VzdF9hdGxfbm9ydGhfZGF0YSA8LSBkYXRhLnRhYmxlKGxhdGl0dWRlX21pZCA9IHVuaXF1ZSh3ZXN0X2F0bF9zaGVsZl9hcmVhc1tsYXRpdHVkZV9lbmQ+MCxdJGxhdGl0dWRlX21pZCkpCgp3ZXN0X2F0bF9ub3J0aF9kYXRhWyxhcmVhXzEwMDBzIDo9IHdlc3RfYXRsX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzFdXS8xMDAwK3dlc3RfYXRsX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzJdXS8xMDAwKmxhdGl0dWRlX21pZF0KCndlc3RfYXRsX3NvdXRoX2RhdGEgPC0gZGF0YS50YWJsZShsYXRpdHVkZV9taWQgPSB1bmlxdWUod2VzdF9hdGxfc2hlbGZfYXJlYXNbbGF0aXR1ZGVfZW5kIDwwLF0kbGF0aXR1ZGVfbWlkKSkKCndlc3RfYXRsX3NvdXRoX2RhdGFbLGFyZWFfMTAwMHMgOj0gd2VzdF9hdGxfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMV1dLzEwMDArd2VzdF9hdGxfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMl1dLzEwMDAqbGF0aXR1ZGVfbWlkXQoKYXJlYV9sYXRpdHVkZV93ZXN0X2F0bCAgPC0gZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gd2VzdF9hdGxfbm9ydGhfZGF0YSwgYWVzKHg9bGF0aXR1ZGVfbWlkLCB5ID0gYXJlYV8xMDAwcyksIGNvbG9yID0gImdyYXk2MCIsIHNpemUgPSAwLjgsIGxpbmV0eXBlID0gImxvbmdkYXNoIikgKwogIGdlb21fbGluZShkYXRhID0gd2VzdF9hdGxfc291dGhfZGF0YSwgYWVzKHg9LWxhdGl0dWRlX21pZCwgeSA9IGFyZWFfMTAwMHMpLCBjb2xvciA9ICJncmF5NjAiLCBzaXplID0gMC44LCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpICsKICBnZW9tX3BvaW50KGRhdGEgPSB3ZXN0X2F0bF9zaGVsZl9hcmVhcywgYWVzKHg9bGF0aXR1ZGVfc3RhcnQsIHk9YXJlYV8xMDAwcyksIHNoYXBlID0xOCwgc2l6ZSA9IDAuNykgKyAKICBnZW9tX2xpbmUoZGF0YSA9IHdlc3RfYXRsX3NoZWxmX2FyZWFzLCBhZXMoeD1sYXRpdHVkZV9zdGFydCwgeT1hcmVhXzEwMDBzKSwgc2l6ZSA9IDAuNykgKwogIGdlb21fcnVnKGRhdGEgPSB3ZXN0X2F0bF9zaGVsZl9hcmVhc19oaWdobGlnaHQsIGFlcyh4ID0gbGF0aXR1ZGVfc3RhcnQsIGNvbG9yID0gYXMuZmFjdG9yKGNoYW5nZV9hYm92ZV8yZm9sZCkpKSArCiBsYWJzKHg9cGFzdGUwKCJMYXRpdHVkZSAiLCJcdTAwQjAiLCJFIiksIHkgPSBleHByZXNzaW9uKHBhc3RlKCJBcmVhICgxMDAwcyBvZiAiLCBrbV57Mn0sIikiKSkpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2Qob3B0aW9uID0gIkEiLCBiZWdpbiA9IDAuMywgZW5kID0gMC43LCBuYW1lID0gIkFzc29jaWF0ZWQgQ2hhbmdlXG5pbiBTaGVsZiBBcmVhIiwgbGFiZWxzID0gYygiQ29udHJhY3Rpb24iLCAiRXhwYW5zaW9uIikpICsKICAjYW5ub3RhdGUoInRleHQiLCB4ID0gODAsIHkgPSAxMjAwMDAsIGxhYmVsID0gIldlc3Rlcm4gQXRsYW50aWMgT2NlYW4iKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCkgKwogIHhsaW0obWluKHdlc3RfYXRsX3NoZWxmX2FyZWFzJGxhdGl0dWRlX2VuZCksIG1heCh3ZXN0X2F0bF9zaGVsZl9hcmVhcyRsYXRpdHVkZV9lbmQpKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gbWFyZ2luKDEwLCA0MCwgMTAsIDEwKSkKCiAgZ2dzYXZlKGFyZWFfbGF0aXR1ZGVfd2VzdF9hdGwsIGZpbGVuYW1lID0gImFyZWFfbGF0aXR1ZGVfd2VzdF9hdGxfMmRlZ3JlZXMuanBnIiwgaGVpZ2h0ID0gNCwgdW5pdHMgPSBjKCJpbiIpKQoKICAKZWFzdF9hdGxfbm9ydGhfZGF0YSA8LSBkYXRhLnRhYmxlKGxhdGl0dWRlX21pZCA9IHVuaXF1ZShlYXN0X2F0bF9zaGVsZl9hcmVhc1tsYXRpdHVkZV9lbmQ+MCxdJGxhdGl0dWRlX21pZCkpCgplYXN0X2F0bF9ub3J0aF9kYXRhWyxhcmVhXzEwMDBzIDo9IGVhc3RfYXRsX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzFdXS8xMDAwK2Vhc3RfYXRsX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzJdXS8xMDAwKmxhdGl0dWRlX21pZF0KCmVhc3RfYXRsX3NvdXRoX2RhdGEgPC0gZGF0YS50YWJsZShsYXRpdHVkZV9taWQgPSB1bmlxdWUoZWFzdF9hdGxfc2hlbGZfYXJlYXNbbGF0aXR1ZGVfZW5kIDwwLF0kbGF0aXR1ZGVfbWlkKSkKCmVhc3RfYXRsX3NvdXRoX2RhdGFbLGFyZWFfMTAwMHMgOj0gZWFzdF9hdGxfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMV1dLzEwMDArZWFzdF9hdGxfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMl1dLzEwMDAqbGF0aXR1ZGVfbWlkXSAgCgphcmVhX2xhdGl0dWRlX2Vhc3RfYXRsICA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBlYXN0X2F0bF9ub3J0aF9kYXRhLCBhZXMoeD1sYXRpdHVkZV9taWQsIHkgPSBhcmVhXzEwMDBzKSwgY29sb3IgPSAiZ3JheTYwIiwgc2l6ZSA9IDAuOCwgbGluZXR5cGUgPSAibG9uZ2Rhc2giKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBlYXN0X2F0bF9zb3V0aF9kYXRhLCBhZXMoeD0tbGF0aXR1ZGVfbWlkLCB5ID0gYXJlYV8xMDAwcyksIGNvbG9yID0gImdyYXk2MCIsIHNpemUgPSAwLjgsIGxpbmV0eXBlID0gImxvbmdkYXNoIikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGVhc3RfYXRsX3NoZWxmX2FyZWFzLCBhZXMoeD1sYXRpdHVkZV9zdGFydCwgeT1hcmVhXzEwMDBzKSwgc2hhcGUgPTE4LCBzaXplID0gMC43KSArIAogIGdlb21fbGluZShkYXRhID0gZWFzdF9hdGxfc2hlbGZfYXJlYXMsIGFlcyh4PWxhdGl0dWRlX3N0YXJ0LCB5PWFyZWFfMTAwMHMpLCBzaXplID0gMC43KSArCiAgZ2VvbV9ydWcoZGF0YSA9IGVhc3RfYXRsX3NoZWxmX2FyZWFzX2hpZ2hsaWdodCwgYWVzKHggPSBsYXRpdHVkZV9zdGFydCwgY29sb3IgPSBhcy5mYWN0b3IoY2hhbmdlX2Fib3ZlXzJmb2xkKSkpICsKIGxhYnMoeD1wYXN0ZTAoIkxhdGl0dWRlICIsIlx1MDBCMCIsIkUiKSwgeSA9IGV4cHJlc3Npb24ocGFzdGUoIkFyZWEgKDEwMDBzIG9mICIsIGttXnsyfSwiKSIpKSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChvcHRpb24gPSAiQSIsIGJlZ2luID0gMC4zLCBlbmQgPSAwLjcsIG5hbWUgPSAiQXNzb2NpYXRlZCBDaGFuZ2VcbmluIFNoZWxmIEFyZWEiLCBsYWJlbHMgPSBjKCJDb250cmFjdGlvbiIsICJFeHBhbnNpb24iKSkgKwogICNhbm5vdGF0ZSgidGV4dCIsIHggPSA4Mi41LCB5ID0gMTEwMDAwLCBsYWJlbCA9ICJFYXN0ZXJuIEF0bGFudGljIE9jZWFuIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDApICsKICB4bGltKG1pbihlYXN0X2F0bF9zaGVsZl9hcmVhcyRsYXRpdHVkZV9lbmQpLCBtYXgoZWFzdF9hdGxfc2hlbGZfYXJlYXMkbGF0aXR1ZGVfZW5kKSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbigxMCwgNDAsIDEwLCAxMCkpCgogIGdnc2F2ZShhcmVhX2xhdGl0dWRlX2Vhc3RfYXRsLCBmaWxlbmFtZSA9ICJhcmVhX2xhdGl0dWRlX2Vhc3RfYXRsXzJkZWdyZWVzLmpwZyIsIGhlaWdodCA9IDQsIHVuaXRzID0gYygiaW4iKSkKICAKICAKZWFzdF9wYWNfbm9ydGhfZGF0YSA8LSBkYXRhLnRhYmxlKGxhdGl0dWRlX21pZCA9IHVuaXF1ZShlYXN0X3BhY19zaGVsZl9hcmVhc1tsYXRpdHVkZV9lbmQ+MCxdJGxhdGl0dWRlX21pZCkpCgplYXN0X3BhY19ub3J0aF9kYXRhWyxhcmVhXzEwMDBzIDo9IGVhc3RfcGFjX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzFdXS8xMDAwK2Vhc3RfcGFjX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzJdXS8xMDAwKmxhdGl0dWRlX21pZF0KCmVhc3RfcGFjX3NvdXRoX2RhdGEgPC0gZGF0YS50YWJsZShsYXRpdHVkZV9taWQgPSB1bmlxdWUoZWFzdF9wYWNfc2hlbGZfYXJlYXNbbGF0aXR1ZGVfZW5kIDwwLF0kbGF0aXR1ZGVfbWlkKSkKCmVhc3RfcGFjX3NvdXRoX2RhdGFbLGFyZWFfMTAwMHMgOj0gZWFzdF9wYWNfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMV1dLzEwMDArZWFzdF9wYWNfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMl1dLzEwMDAqbGF0aXR1ZGVfbWlkXQoKYXJlYV9sYXRpdHVkZV9lYXN0X3BhYyAgPC0gZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gZWFzdF9wYWNfbm9ydGhfZGF0YSwgYWVzKHg9bGF0aXR1ZGVfbWlkLCB5ID0gYXJlYV8xMDAwcyksIGNvbG9yID0gImdyYXk2MCIsIHNpemUgPSAwLjgsIGxpbmV0eXBlID0gImxvbmdkYXNoIikgKwojICBnZW9tX2xpbmUoZGF0YSA9IGVhc3RfcGFjX3NvdXRoX2RhdGEsIGFlcyh4PWxhdGl0dWRlX21pZCwgeSA9IGFyZWFfMTAwMHMpLCBjb2xvciA9ICJncmF5NjAiLCBzaXplID0gMC44LCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpICsgbm90IHNpZ25pZmljYW50CiAgZ2VvbV9wb2ludChkYXRhID0gZWFzdF9wYWNfc2hlbGZfYXJlYXMsIGFlcyh4PWxhdGl0dWRlX3N0YXJ0LCB5PWFyZWFfMTAwMHMpLCBzaGFwZSA9MTgsIHNpemUgPSAwLjcpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSBlYXN0X3BhY19zaGVsZl9hcmVhcywgYWVzKHg9bGF0aXR1ZGVfc3RhcnQsIHk9YXJlYV8xMDAwcyksIHNpemUgPSAwLjcpICsKICBnZW9tX3J1ZyhkYXRhID0gZWFzdF9wYWNfc2hlbGZfYXJlYXNfaGlnaGxpZ2h0LCBhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCBjb2xvciA9IGFzLmZhY3RvcihjaGFuZ2VfYWJvdmVfMmZvbGQpKSkgKwogbGFicyh4PXBhc3RlMCgiTGF0aXR1ZGUgIiwiXHUwMEIwIiwiRSIpLCB5ID0gZXhwcmVzc2lvbihwYXN0ZSgiQXJlYSAoMTAwMHMgb2YgIiwga21eezJ9LCIpIikpKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG9wdGlvbiA9ICJBIiwgYmVnaW4gPSAwLjMsIGVuZCA9IDAuNywgbmFtZSA9ICJBc3NvY2lhdGVkIENoYW5nZVxuaW4gU2hlbGYgQXJlYSIsIGxhYmVscyA9IGMoIkNvbnRyYWN0aW9uIiwgIkV4cGFuc2lvbiIpKSArCiAgI2Fubm90YXRlKCJ0ZXh0IiwgeCA9IDgwLCB5ID0gMTMwMDAwLCBsYWJlbCA9ICJFYXN0ZXJuIFBhY2lmaWMgT2NlYW4iKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCkgKwogIHhsaW0obWluKGVhc3RfcGFjX3NoZWxmX2FyZWFzJGxhdGl0dWRlX2VuZCksIG1heChlYXN0X3BhY19zaGVsZl9hcmVhcyRsYXRpdHVkZV9lbmQpKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gbWFyZ2luKDEwLCA0MCwgMTAsIDEwKSkKCiAgZ2dzYXZlKGFyZWFfbGF0aXR1ZGVfZWFzdF9wYWMsIGZpbGVuYW1lID0gImFyZWFfbGF0aXR1ZGVfZWFzdF9wYWNfMmRlZ3JlZXMuanBnIiwgaGVpZ2h0ID0gNCwgdW5pdHMgPSBjKCJpbiIpKQoKICAKd2VzdF9wYWNfbm9ydGhfZGF0YSA8LSBkYXRhLnRhYmxlKGxhdGl0dWRlX21pZCA9IHVuaXF1ZSh3ZXN0X3BhY19zaGVsZl9hcmVhc1tsYXRpdHVkZV9lbmQ+MCxdJGxhdGl0dWRlX21pZCkpCgp3ZXN0X3BhY19ub3J0aF9kYXRhWyxhcmVhXzEwMDBzIDo9IHdlc3RfcGFjX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzFdXS8xMDAwK3dlc3RfcGFjX25vcnRoX21vZCRjb2VmZmljaWVudHNbWzJdXS8xMDAwKmxhdGl0dWRlX21pZF0KCndlc3RfcGFjX3NvdXRoX2RhdGEgPC0gZGF0YS50YWJsZShsYXRpdHVkZV9taWQgPSB1bmlxdWUod2VzdF9wYWNfc2hlbGZfYXJlYXNbbGF0aXR1ZGVfZW5kIDwwLF0kbGF0aXR1ZGVfbWlkKSkKCndlc3RfcGFjX3NvdXRoX2RhdGFbLGFyZWFfMTAwMHMgOj0gd2VzdF9wYWNfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMV1dLzEwMDArd2VzdF9wYWNfc291dGhfbW9kJGNvZWZmaWNpZW50c1tbMl1dLzEwMDAqbGF0aXR1ZGVfbWlkXQoKKGFyZWFfbGF0aXR1ZGVfd2VzdF9wYWMgPC0gZ2dwbG90KCkgKwogIGdlb21fbGluZShkYXRhID0gd2VzdF9wYWNfbm9ydGhfZGF0YSwgYWVzKHg9bGF0aXR1ZGVfbWlkLCB5ID0gYXJlYV8xMDAwcyksIGNvbG9yID0gImdyYXk2MCIsIHNpemUgPSAwLjgsIGxpbmV0eXBlID0gImxvbmdkYXNoIikgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9ICJncmF5NjAiLCBsYWJlbHMgPSAiQXJlYSB+IExhdGl0dWRlXG5MaW5lYXIgUmVncmVzc2lvbiIpICsKICBnZW9tX2xpbmUoZGF0YSA9IHdlc3RfcGFjX3NvdXRoX2RhdGEsIGFlcyh4PS1sYXRpdHVkZV9taWQsIHkgPSBhcmVhXzEwMDBzKSwgY29sb3IgPSAiZ3JheTYwIiwgc2l6ZSA9IDAuOCwgbGluZXR5cGUgPSAibG9uZ2Rhc2giKSArCiAgZ2VvbV9wb2ludChkYXRhID0gd2VzdF9wYWNfc2hlbGZfYXJlYXMsIGFlcyh4PWxhdGl0dWRlX3N0YXJ0LCB5PWFyZWFfMTAwMHMpLCBzaGFwZSA9MTgsIHNpemUgPSAwLjcpICsgCiAgZ2VvbV9saW5lKGRhdGEgPSB3ZXN0X3BhY19zaGVsZl9hcmVhcywgYWVzKHg9bGF0aXR1ZGVfc3RhcnQsIHk9YXJlYV8xMDAwcyksIHNpemUgPSAwLjcpICsKICBnZW9tX3J1ZyhkYXRhID0gd2VzdF9wYWNfc2hlbGZfYXJlYXNfaGlnaGxpZ2h0LCBhZXMoeCA9IGxhdGl0dWRlX3N0YXJ0LCBjb2xvciA9IGFzLmZhY3RvcihjaGFuZ2VfYWJvdmVfMmZvbGQpKSkgKwogbGFicyh4PXBhc3RlMCgiTGF0aXR1ZGUgIiwiXHUwMEIwIiwiRSIpLCB5ID0gZXhwcmVzc2lvbihwYXN0ZSgiQXJlYSAoMTAwMHMgb2YgIiwga21eezJ9LCIpIikpKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG9wdGlvbiA9ICJBIiwgYmVnaW4gPSAwLjMsIGVuZCA9IDAuNywgbmFtZSA9ICJBc3NvY2lhdGVkIENoYW5nZVxuaW4gU2hlbGYgQXJlYSIsIGxhYmVscyA9IGMoIkNvbnRyYWN0aW9uIiwgIkV4cGFuc2lvbiIpKSArCiAgI2Fubm90YXRlKCJ0ZXh0IiwgeCA9IDkwLCB5ID0gMTMwMDAwLCBsYWJlbCA9ICJXZXN0ZXJuIFBhY2lmaWMgT2NlYW4iKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCkgKwogIHhsaW0obWluKHdlc3RfcGFjX3NoZWxmX2FyZWFzJGxhdGl0dWRlX2VuZCksIG1heCh3ZXN0X3BhY19zaGVsZl9hcmVhcyRsYXRpdHVkZV9lbmQpKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gbWFyZ2luKDEwLCA0MCwgMTAsIDEwKSkpCgogIAoKICBnZ3NhdmUoYXJlYV9sYXRpdHVkZV93ZXN0X3BhYywgZmlsZW5hbWUgPSAiYXJlYV9sYXRpdHVkZV93ZXN0X3BhY18yZGVncmVlcy5qcGciLCBoZWlnaHQgPSA0LCB1bml0cyA9IGMoImluIikpCiAgCiAgI3B1bGwgZ2VuZXJpYyBydWcgbGVnZW5kIG9ubHkKYXJlYV9sYXRpdHVkZV9ydWdfbGVnZW5kIDwtIGdldF9sZWdlbmQoYXJlYV9sYXRpdHVkZV93ZXN0X3BhYykKc2F2ZShhcmVhX2xhdGl0dWRlX3J1Z19sZWdlbmQsIGZpbGUgPSBoZXJlOjpoZXJlKCJGaWd1cmVzIiwgIkZpZ3VyZTNfNSIsICJhcmVhX2xhdGl0dWRlX3J1Z19sZWdlbmQuUkRhdGEiKSkKCmBgYApEdW1ieSBncmFwaCB0byBqdXN0IG1ha2UgYWRkaXRpb25hbCBsZWdlbmQgY29tcG9uZW50CmBgYHtyIGFkZGl0aW9uYWwgbGVnZW5kIGNvbXBvbmVudH0KbGVnZW5kX2xhdF9hcmVhX3JlZ3Jlc3Npb24gPC0gZ2V0X2xlZ2VuZCgKICBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSB3ZXN0X3BhY19ub3J0aF9kYXRhLCBhZXMoeD1sYXRpdHVkZV9taWQsIHkgPSBhcmVhXzEwMDBzLCBjb2xvciA9ICJ2YWx1ZSIpLCBzaXplID0gMC44LCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSAiZ3JheTYwIiwgbGFiZWxzID0gIkFyZWEgfiBMYXRpdHVkZVxuTGluZWFyIFJlZ3Jlc3Npb24iKSArCiAgICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSBtYXJnaW4oMTAsIDQwLCAxMCwgMTApLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpKQoKc2F2ZShsZWdlbmRfbGF0X2FyZWFfcmVncmVzc2lvbiwgZmlsZSA9IGhlcmU6OmhlcmUoIkZpZ3VyZXMiLCAiRmlndXJlM181IiwgImxlZ2VuZF9sYXRfYXJlYV9yZWdyZXNzaW9uLlJEYXRhIikpCmBgYAoKSG93IG1hbnkgZXhwZXJpZW5jZSAnc2lnbmlmaWNhbnQnIGNoYW5nZXMgaW4gaGFiaXRhdCAoYXQgbGVhc3QgLTUwJSBvciArMTAwJSBjaGFuZ2UgZnJvbSBvbmUgYmluIHRvIGFub3RoZXIpIEkgd2lsbCBnbyB3aXRoIElVQ04gNTAlIGxvc3MgLT4gdnVsbmVyYWJsZSBzcGVjaWVzIGRlc2lnbmF0aW9uLgoKQmluIHNoaWZ0cyAtLT4gY29udHJhY3Rpb25zIChsb3NzIG9mIDUwJSkgdmVyc3VzIGV4cGFuc2lvbnMgKGdhaW4gb2YgMjAwJSkgdmVyc3VzIG5ldXRyYWwKYGBge3IgYmluIHNoaWZ0IGNhdGVnb3JpemF0aW9ufQoKbGlicmFyeShnZ3JlcGVsKQojY2FsbCBhbGwgb2JqZWN0cyBpbiBlbnZpcm9ubWVudCB3aXRoICJzdGF0cyIgc3RyaW5nCnN0YXRzX3N0cmluZzwtZ3JlcCgiYXJlYXNfc3RhdHMiLG5hbWVzKC5HbG9iYWxFbnYpLHZhbHVlPVRSVUUpCnN0YXRzX3N0cmluZ19saXN0PC1kby5jYWxsKCJsaXN0IixtZ2V0KHN0YXRzX3N0cmluZykpCm5hbWVzKHN0YXRzX3N0cmluZ19saXN0KSAjY2hlY2sKCiNyb3duYW1lcwpzdGF0c19zdHJpbmdfcm93bmFtZXMgPC0gcmVwKG5hbWVzKHN0YXRzX3N0cmluZ19saXN0KSwgZWFjaCA9IDMpCnN0YXRzX3N0cmluZ190eXBlIDwtIHJlcChjKCJjb250cmFjdGlvbiIsICJuZXV0cmFsIiwgImV4cGFuc2lvbiIpLHRpbWVzID0gNikKCiNjaGVjayA8LSBjKCJFYXN0ZXJuIEluZGlhbiBPY2VhbiIgLCJXZXN0ZXJuIFBhY2lmaWMgT2NlYW4iICwiRWFzdGVybiBQYWNpZmljIE9jZWFuIiAsIldlc3Rlcm4gQXRsYW50aWMgT2NlYW4iICwiV2VzdGVybiBJbmRpYW4gT2NlYW4iICwiRWFzdGVybiBBdGxhbnRpYyBPY2VhbiIpCgpzaWduaWZpY2FudF9jaGFuZ2VzIDwtIGFzLmRhdGEudGFibGUocmJpbmQoc3RhdHNfc3RyaW5nX2xpc3RbWzFdXSxzdGF0c19zdHJpbmdfbGlzdFtbMl1dLHN0YXRzX3N0cmluZ19saXN0W1szXV0sc3RhdHNfc3RyaW5nX2xpc3RbWzRdXSxzdGF0c19zdHJpbmdfbGlzdFtbNV1dLHN0YXRzX3N0cmluZ19saXN0W1s2XV0pKQoKc2lnbmlmaWNhbnRfY2hhbmdlc1ssIHJlZ2lvbiA6PSBzdGF0c19zdHJpbmdfcm93bmFtZXNdWyx0eXBlIDo9IHN0YXRzX3N0cmluZ190eXBlXQoKI21lbHQgdG8gcGxvdApzaWduaWZpY2FudF9jaGFuZ2VzLmxvbmcgPC0gbWVsdChzaWduaWZpY2FudF9jaGFuZ2VzLCBpZC52YXJzID0gYygicmVnaW9uIiwidHlwZSIpLCB2YXJpYWJsZS5uYW1lID0gImhlbWlzcGhlcmUiLCBtZWFzdXJlLnZhcnMgPSBjKCJub3J0aCIsICJzb3V0aCIpKQoKI2NhbGN1bGF0ZSBwZXJjZW50YWdlcwpzaWduaWZpY2FudF9jaGFuZ2VzLmxvbmdbLHRvdGFsX2JpbnNfaGVtaXNwaGVyZSA6PSBzdW0odmFsdWUpLC4ocmVnaW9uLCBoZW1pc3BoZXJlKV1bLHRvdGFsX2JpbnNfcmVnaW9uIDo9IHN1bSh2YWx1ZSkscmVnaW9uXVsscGVyY2VudF9ieV9oZW1pc3BoZXJlIDo9IHJvdW5kKHN1bSh2YWx1ZSkvdG90YWxfYmluc19oZW1pc3BoZXJlKjEwMCwyKSwuKHJlZ2lvbixoZW1pc3BoZXJlLHR5cGUpXVsscGVyY2VudF9ieV9yZWdpb24gOj0gcm91bmQoc3VtKHZhbHVlKS90b3RhbF9iaW5zX3JlZ2lvbioxMDAsMiksIC4ocmVnaW9uLHR5cGUpXQoKc2lnbmlmaWNhbnRfY2hhbmdlcy5sb25nWyx0eXBlIDo9IGZhY3Rvcih0eXBlLCBsZXZlbHMgPSBjKCJjb250cmFjdGlvbiIsIm5ldXRyYWwiLCJleHBhbnNpb24iKSldWyxyZWdpb25fbmFtZXMgOj0gZmFjdG9yKHJlZ2lvbiwgbGV2ZWxzID0gYygiZWFzdF9hdGxfc2hlbGZfYXJlYXNfc3RhdHMiLCAid2VzdF9hdGxfc2hlbGZfYXJlYXNfc3RhdHMiLCAiZWFzdF9pbmRfc2hlbGZfYXJlYXNfc3RhdHMiLCAid2VzdF9pbmRfc2hlbGZfYXJlYXNfc3RhdHMiICwiZWFzdF9wYWNfc2hlbGZfYXJlYXNfc3RhdHMiICwid2VzdF9wYWNfc2hlbGZfYXJlYXNfc3RhdHMiKSwgbGFiZWxzID0gYygiRWFzdCBBdGxhbnRpYyIsICJXZXN0IEF0bGFudGljIiwgIkVhc3QgSW5kaWFuIiwgIldlc3QgSW5kaWFuIiAsIkVhc3QgUGFjaWZpYyIgLCJXZXN0IFBhY2lmaWMiKSldWyxoZW1pc3BoZXJlIDo9IGZhY3RvcihoZW1pc3BoZXJlLGxldmVscyA9IGMoIm5vcnRoIiwic291dGgiKSwgbGFiZWxzID0gYygiTm9ydGgiLCJTb3V0aCIpKV0KCgoKCmJsYW5rX3RoZW1lIDwtIHRoZW1lX21pbmltYWwoKSsKICB0aGVtZSgKICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICBwYW5lbC5ncmlkPWVsZW1lbnRfYmxhbmsoKSwKICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTQsIGZhY2U9ImJvbGQiKQogICkKCiN2aXJpZGlzIG1hZ21hIHBhbGV0dGUsIENvbnRyYWN0aW9ucyBbMV0sIGV4cGFuc2lvbnNbMl0pCnZpcmlkaXNfZXhwX2NvbnQgPC12aXJpZGlzX3BhbChiZWdpbiA9IDAuMywgZW5kID0gMC43LCBvcHRpb24gPSAiQSIpKDIpCgojYnkgaGVtaXNwaGVyZQoKZ2dwbG90KGRhdGEgPSBzaWduaWZpY2FudF9jaGFuZ2VzLmxvbmcsIGFlcyh4PSIiLCB5ID0gcGVyY2VudF9ieV9oZW1pc3BoZXJlLCBmaWxsID0gdHlwZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxLCBhbHBoYSA9IDAuOCkgKwogIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQ9MCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGModmlyaWRpc19leHBfY29udFsxXSwgImdyZXk5NCIsIHZpcmlkaXNfZXhwX2NvbnRbMl0pLCBuYW1lID0gIlNoZWxmIEFyZWEgQ2hhbmdlIiwgbGFiZWxzID0gYygiQ29udHJhY3Rpb24iLCAiTmVpdGhlciIsICJFeHBhbnNpb24iKSkgKwogIGZhY2V0X2dyaWQoaGVtaXNwaGVyZX5yZWdpb25fbmFtZXMpICsKICBnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKHBlcmNlbnRfYnlfaGVtaXNwaGVyZSwxKSwiJSIpKSwgc2l6ZSA9IDEsIGZvbnRmYWNlID0gImJvbGQiLCBwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC4zKSkgKwogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kID0gYygwLDApKSArCiAgYmxhbmtfdGhlbWUgKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLCAKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNykpICsKICBndWlkZXMoc2hhcGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gMC41KSksIAogICAgICAgICBmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDAuNSkpKQoKZ2dzYXZlKGZpbGVuYW1lID0gImZpZ3VyZTZfaGFiaXRhdGxvc3NfZ2Fpbl8yZm9sZF9ieWhlbWlzcGhlcmUuanBnIiwgaGVpZ2h0ID0gMiwgd2lkdGggPSA2LCB1bml0ID0gImluIikKZ2dzYXZlKGZpbGVuYW1lID0gImZpZ3VyZTZfaGFiaXRhdGxvc3NfZ2Fpbl8yZm9sZF9ieWhlbWlzcGhlcmUuZXBzIiwgaGVpZ2h0ID0gMiwgd2lkdGggPSA2LCB1bml0ID0gImluIikKCgojYnkgcmVnaW9uIChoZW1pc3BoZXJlJ3MgY29tcHJlc3NlZCkKZ2dwbG90KGRhdGEgPSB1bmlxdWUoc2lnbmlmaWNhbnRfY2hhbmdlcy5sb25nWywuKHJlZ2lvbl9uYW1lcyx0eXBlLHBlcmNlbnRfYnlfcmVnaW9uKV0pLCBhZXMoeD0iIiwgeSA9IHBlcmNlbnRfYnlfcmVnaW9uLCBmaWxsID0gdHlwZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArCiAgY29vcmRfcG9sYXIoInkiLCBzdGFydD0wKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYyh2aXJpZGlzX2V4cF9jb250WzFdLCAiZ3JleTk0IiwgdmlyaWRpc19leHBfY29udFsyXSksIG5hbWUgPSAiU2hlbGYgQXJlYSBDaGFuZ2UiLCBsYWJlbHMgPSBjKCJDb250cmFjdGlvbiIsICJOZWl0aGVyIiwgIkV4cGFuc2lvbiIpKSArCiAgZmFjZXRfd3JhcCh+cmVnaW9uX25hbWVzKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMChwZXJjZW50X2J5X3JlZ2lvbiwiJSIpKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuMSksIHNpemUgPSAyKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShleHBhbmQgPSBjKDAsMCkpICsKICBibGFua190aGVtZSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpKQoKZ2dzYXZlKGZpbGVuYW1lID0gImZpZ3VyZTZfaGFiaXRhdGxvc3NfZ2Fpbl8yZm9sZF9ieXJlZ2lvbi5qcGciLCBoZWlnaHQgPSA0LCB3aWR0aCA9IDgsIHVuaXQgPSAiaW4iKQpnZ3NhdmUoZmlsZW5hbWUgPSAiZmlndXJlNl9oYWJpdGF0bG9zc19nYWluXzJmb2xkX2J5cmVnaW9uLmVwcyIsIGhlaWdodCA9IDQsIHdpZHRoID0gOCwgdW5pdCA9ICJpbiIpCgoKYGBgCgpOb3csIEkgc2hvdWxkIG1ha2UgbWFwcyBmb3IgZWFjaCBvZiB0aGVzZSByZWdpb25zCgpVc2VkIGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL3ZhbGVudGluaXRuZWxhdi9jNzU5OGZjZmM4ZTUzNjU4ZjY2ZmVlYTlkM2JhZmI0MCBmb3IgaW5zdHJ1Y3Rpb25zCgpgYGB7ciBzZXR1cCB3b3JsZCBtYXBzfQpsaWJyYXJ5KGdnc3BhdGlhbCkKCiAgd29ybGQgPC0gbmVfY291bnRyaWVzKHNjYWxlID0gIm1lZGl1bSIsIHJldHVybmNsYXNzID0gInNmIikKCiMgfn5+fn5+fn5+fn4gRG93bmxvYWQgc2hhcGVmaWxlIGZyb20gd3d3Lm5hdHVyYWxlYXJ0aGRhdGEuY29tIH5+fn5+fn5+fn5+ICMKIyBEb3dubG9hZCBjb3VudHJpZXMgZGF0YQojZG93bmxvYWQuZmlsZSh1cmwgPSAiaHR0cDovL3d3dy5uYXR1cmFsZWFydGhkYXRhLmNvbS9odHRwLy93d3cubmF0dXJhbGVhcnRoZGF0YS5jb20vZG93bmxvYWQvMTEwbS9jdWx0dXJhbC9uZV8xMTBtX2FkbWluXzBfY291bnRyaWVzLnppcCIsIAojICAgICAgICAgICAgICBkZXN0ZmlsZSA9ICJuZV8xMTBtX2FkbWluXzBfY291bnRyaWVzLnppcCIpCiMgdW56aXAgdGhlIHNoYXBlZmlsZSBpbiB0aGUgZGlyZWN0b3J5IG1lbnRpb25lZCB3aXRoICJleGRpciIgYXJndW1lbnQKI3VuemlwKHppcGZpbGU9Im5lXzExMG1fYWRtaW5fMF9jb3VudHJpZXMuemlwIiwgZXhkaXIgPSAibmVfMTEwbV9hZG1pbl8wX2NvdW50cmllcyIpCiMgZGVsZXRlIHRoZSB6aXAgZmlsZQojZmlsZS5yZW1vdmUoIm5lXzExMG1fYWRtaW5fMF9jb3VudHJpZXMuemlwIikKIyByZWFkIHRoZSBzaGFwZWZpbGUgd2l0aCByZWFkT0dSIGZyb20gcmdkYWwgcGFja2FnZQpORV9jb3VudHJpZXMgPC0gcmVhZE9HUihkc24gPSAibmVfMTEwbV9hZG1pbl8wX2NvdW50cmllcyIsIGxheWVyID0gIm5lXzExMG1fYWRtaW5fMF9jb3VudHJpZXMiKQpjbGFzcyhORV9jb3VudHJpZXMpICMgaXMgYSBTcGF0aWFsUG9seWdvbnNEYXRhRnJhbWUgb2JqZWN0CgojIH5+fn5+fn5+fn5+IFNwbGl0IHdvcmxkIG1hcCBieSAic3BsaXQgbGluZSIgfn5+fn5+fn5+fn4gIwoKIyBzaGlmdCBjZW50cmFsL3ByaW1lIG1lcmlkaWFuIHRvd2FyZHMgd2VzdCDigJMgcG9zaXRpdmUgdmFsdWVzIG9ubHkKc2hpZnQgPC0gMTgwICszMAoKIyBjcmVhdGUgInNwbGl0IGxpbmUiIHRvIHNwbGl0IGNvdW50cnkgcG9seWdvbnMKV0dTODQgPC0gQ1JTKCIrcHJvaj1sb25nbGF0ICtkYXR1bT1XR1M4NCArbm9fZGVmcyArZWxscHM9V0dTODQgK3Rvd2dzODQ9MCwwLDAiKQpzcGxpdC5saW5lIDwtIFNwYXRpYWxMaW5lcyhsaXN0KExpbmVzKGxpc3QoTGluZShjYmluZCgxODAtc2hpZnQsYygtOTAsOTApKSkpLCBJRD0ibGluZSIpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvajRzdHJpbmc9V0dTODQpCgojIE5PVEUgLSBpbiBjYXNlIG9mIFRvcG9sb2d5RXhjZXB0aW9uJyBlcnJvcnMgd2hlbiBpbnRlcnNlY3RpbmcgbGluZSB3aXRoIGNvdW50cnkgcG9seWdvbnMsCiMgYXBwbHkgdGhlIGdCdWZmZXIgc29sdXRpb24gc3VnZ2VzdGVkIGF0OgojIGh0dHA6Ly9naXMuc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzE2MzQ0NS9yLXNvbHV0aW9uLWZvci10b3BvbG9neWV4Y2VwdGlvbi1pbnB1dC1nZW9tLTEtaXMtaW52YWxpZC1zZWxmLWludGVyc2VjdGlvbi1lcgpORV9jb3VudHJpZXMgPC0gZ0J1ZmZlcihORV9jb3VudHJpZXMsIGJ5aWQ9VFJVRSwgd2lkdGg9MCkKCiMgaW50ZXJzZWN0aW5nIGxpbmUgd2l0aCBjb3VudHJ5IHBvbHlnb25zCmxpbmUuZ0ludCA8LSBnSW50ZXJzZWN0aW9uKHNwbGl0LmxpbmUsIE5FX2NvdW50cmllcykKCiMgY3JlYXRlIGEgdmVyeSB0aGluIHBvbHlnb24gKGJ1ZmZlcikgb3V0IG9mIHRoZSBpbnRlcnNlY3RpbmcgInNwbGl0IGxpbmUiCmJmIDwtIGdCdWZmZXIobGluZS5nSW50LCBieWlkPVRSVUUsIHdpZHRoPTAuMDAwMDAxKSAgCgojIHNwbGl0IGNvdW50cnkgcG9seWdvbnMgdXNpbmcgaW50ZXJzZWN0aW5nIHRoaW4gcG9seWdvbiAoYnVmZmVyKQpORV9jb3VudHJpZXMuc3BsaXQgPC0gZ0RpZmZlcmVuY2UoTkVfY291bnRyaWVzLCBiZiwgYnlpZD1UUlVFKQojIHBsb3QoTkVfY291bnRyaWVzLnNwbGl0KSAjIGNoZWNrIG1hcApjbGFzcyhORV9jb3VudHJpZXMuc3BsaXQpICMgaXMgYSBTcGF0aWFsUG9seWdvbnMgb2JqZWN0CgojIH5+fn5+fn5+fn5+IENyZWF0ZSBncmF0aWN1bGVzIH5+fn5+fn5+fn5+ICMKIyBjcmVhdGUgYSBib3VuZGluZyBib3ggLSB3b3JsZCBleHRlbnQKYi5ib3ggPC0gYXMocmFzdGVyOjpleHRlbnQoLTE4MCwgMTgwLCAtOTAsIDkwKSwgIlNwYXRpYWxQb2x5Z29ucyIpCiMgYXNzaWduIENSUyB0byBib3gKcHJvajRzdHJpbmcoYi5ib3gpIDwtIFdHUzg0CiMgY3JlYXRlIGdyYXRpY3VsZXMvZ3JpZCBsaW5lcyBmcm9tIGJveApncmlkIDwtIGdyaWRsaW5lcyhiLmJveCwgCiAgICAgICAgICAgICAgICAgIGVhc3RzICA9IHNlcShmcm9tPS0xODAsIHRvPTE4MCwgYnk9MjApLAogICAgICAgICAgICAgICAgICBub3J0aHMgPSBzZXEoZnJvbT0tOTAsIHRvPTkwLCBieT0xMCkpCgojIGNyZWF0ZSBsYWJlbHMgZm9yIGdyYXRpY3VsZXMKZ3JpZC5sYmwgPC0gbGFiZWxzKGdyaWQsIHNpZGUgPSAxOjQpCgojIHRyYW5zZm9ybSBsYWJlbHMgZnJvbSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lIHRvIGEgZGF0YSB0YWJsZSB0aGF0IGdncGxvdCBjYW4gdXNlCmdyaWQubGJsLkRUIDwtIGRhdGEudGFibGUoZ3JpZC5sYmxAY29vcmRzLCBncmlkLmxibEBkYXRhKQoKIyBwcmVwYXJlIGxhYmVscyB3aXRoIHJlZ3VsYXIgZXhwcmVzc2lvbjoKIyAtIGRlbGV0ZSB1bndhbnRlZCBsYWJlbHMKZ3JpZC5sYmwuRFRbLCBsYWJlbHMgOj0gZ3N1YihwYXR0ZXJuPSIxODBcXCpkZWdyZWV8OTBcXCpkZWdyZWVcXCpOfDkwXFwqZGVncmVlXFwqUyIsIHJlcGxhY2VtZW50PSIiLCB4PWxhYmVscyldCiMgLSByZXBsYWNlIHBhdHRlcm4gIipkZWdyZWUiIHdpdGggIsKwIiAoKiBuZWVkcyB0byBiZSBlc2NhcGVkIHdpdGggXFwpCmdyaWQubGJsLkRUWywgbGJsIDo9IGdzdWIocGF0dGVybj0iXFwqZGVncmVlIiwgcmVwbGFjZW1lbnQ9IsKwIiwgeD1sYWJlbHMpXQojIC0gZGVsZXRlIGFueSByZW1haW5pbmcgIioiCmdyaWQubGJsLkRUWywgbGJsIDo9IGdzdWIocGF0dGVybj0iKlxcKiIsIHJlcGxhY2VtZW50PSIiLCB4PWxibCldCgojIGFkanVzdCBjb29yZGluYXRlcyBvZiBsYWJlbHMgc28gdGhhdCB0aGV5IGZpdCBpbnNpZGUgdGhlIGdsb2JlCmdyaWQubGJsLkRUWywgbG9uZyA6PSBpZmVsc2UoY29vcmRzLngxICVpbiUgYygtMTgwLDE4MCksIGNvb3Jkcy54MSoxNzUvMTgwLCBjb29yZHMueDEpXQpncmlkLmxibC5EVFssIGxhdCAgOj0gaWZlbHNlKGNvb3Jkcy54MiAlaW4lIGMoLTkwLDkwKSwgY29vcmRzLngyKjgyLzkwLCBjb29yZHMueDIpXQoKIyB+fn5+fn5+fn5+fiBQcmVwYXJlIGRhdGEgZm9yIGdncGxvdCwgc2hpZnQgJiBwcm9qZWN0IGNvb3JkaW5hdGVzIH5+fn5+fn5+fn5+ICMKIyBnaXZlIHRoZSBQT1JKLjQgc3RyaW5nIGZvciBFY2tlcnQgSVYgcHJvamVjdGlvbiAoIGNoYW5nZWQgdG8gZGlmZmVyZW50IHByb2plY3Rpb24sICIrcHJvaj1lY2s0ICtsb25fMD0wICt4XzA9MCAreV8wPTAgK2VsbHBzPVdHUzg0ICtkYXR1bT1XR1M4NCArdW5pdHM9bSArbm9fZGVmcyIgZm9yIGVja2VydCkKUFJPSiA8LSAiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgK25vX2RlZnMgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwIiAKCiMgdHJhbnNmb3JtIGdyYXRpY3VsZXMgZnJvbSBTcGF0aWFsTGluZXMgdG8gYSBkYXRhIHRhYmxlIHRoYXQgZ2dwbG90IGNhbiB1c2UKZ3JpZC5EVCA8LSBkYXRhLnRhYmxlKG1hcF9kYXRhKFNwYXRpYWxMaW5lc0RhdGFGcmFtZShzbD1ncmlkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhPWRhdGEuZnJhbWUoMTpsZW5ndGgoZ3JpZCkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXRjaC5JRCA9IEZBTFNFKSkpCiMgcHJvamVjdCBjb29yZGluYXRlcwojIGFzc2lnbiBtYXRyaXggb2YgcHJvamVjdGVkIGNvb3JkaW5hdGVzIGFzIHR3byBjb2x1bW5zIGluIGRhdGEgdGFibGUKZ3JpZC5EVFssIGMoIlgiLCJZIikgOj0gZGF0YS50YWJsZShwcm9qZWN0KGNiaW5kKGxvbmcsIGxhdCksIHByb2o9UFJPSikpXQoKIyBwcm9qZWN0IGNvb3JkaW5hdGVzIG9mIGxhYmVscwpncmlkLmxibC5EVFssIGMoIlgiLCJZIikgOj0gZGF0YS50YWJsZShwcm9qZWN0KGNiaW5kKGxvbmcsIGxhdCksIHByb2o9UFJPSikpXQoKIyB0cmFuc2Zvcm0gc3BsaXQgY291bnRyeSBwb2x5Z29ucyBpbiBhIGRhdGEgdGFibGUgdGhhdCBnZ3Bsb3QgY2FuIHVzZQpDb3VudHJ5LkRUX3NoaWZ0IDwtIGRhdGEudGFibGUobWFwX2RhdGEoYXMoTkVfY291bnRyaWVzLnNwbGl0LCAiU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lIikpKQpDb3VudHJ5LkRUIDwtIGRhdGEudGFibGUobWFwX2RhdGEoYXMoTkVfY291bnRyaWVzLCAiU3BhdGlhbFBvbHlnb25zRGF0YUZyYW1lIikpKQojIFNoaWZ0IGNvb3JkaW5hdGVzCkNvdW50cnkuRFRfc2hpZnRbLCBsb25nLm5ldyA6PSBsb25nICsgc2hpZnRdCkNvdW50cnkuRFRfc2hpZnRbLCBsb25nLm5ldyA6PSBpZmVsc2UobG9uZy5uZXcgPiAxODAsIGxvbmcubmV3LTM2MCwgbG9uZy5uZXcpXQoKIyBwcm9qZWN0IGNvb3JkaW5hdGVzIApDb3VudHJ5LkRUWywgYygiWCIsIlkiKSA6PSBkYXRhLnRhYmxlKHByb2plY3QoY2JpbmQobG9uZywgbGF0KSwgcHJvaj1QUk9KKSldCkNvdW50cnkuRFRfc2hpZnRbLCBjKCJYIiwiWSIpIDo9IGRhdGEudGFibGUocHJvamVjdChjYmluZChsb25nLm5ldywgbGF0KSwgcHJvaj1QUk9KKSldCgojIH5+fn5+fn5+fn5+IFBsb3QgbWFwIH5+fn5+fn5+fn5+ICMKZ2dwbG90KCkgKyAKICAgICMgYWRkIHByb2plY3RlZCBjb3VudHJpZXMKICAgIGdlb21fcG9seWdvbihkYXRhID0gQ291bnRyeS5EVF9zaGlmdCwgCiAgICAgICAgICAgICAgICAgYWVzKHggPSBsb25nLm5ldysxNTAsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCAKICAgICAgICAgICAgICAgICBjb2xvdXIgPSAiZ3JheTcwIiwgCiAgICAgICAgICAgICAgICAgZmlsbCA9ICJncmF5OTAiLCAKICAgICAgICAgICAgICAgICBzaXplID0gMC4yNSkgKwogICAgIyBhZGQgZ3JhdGljdWxlcwogICAgZ2VvbV9wYXRoKGRhdGEgPSBncmlkLkRULCAKICAgICAgICAgICAgICBhZXMoeCA9IFgsIHkgPSBZLCBncm91cCA9IGdyb3VwKSwgCiAgICAgICAgICAgICAgbGluZXR5cGUgPSAiZG90dGVkIiwgY29sb3VyID0gImdyZXk1MCIsIHNpemUgPSAuMjUpICsKICAgICMgYWRkIGEgYm91bmRpbmcgYm94IChzZWxlY3QgZ3JhdGljdWxlcyBhdCBlZGdlcykKICAgIGdlb21fcGF0aChkYXRhID0gZ3JpZC5EVFsobG9uZyAlaW4lIGMoLTE4MCwxODApICYgcmVnaW9uID09ICJOUyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfChsb25nICVpbiUgYygtMTgwLDE4MCkgJiBsYXQgJWluJSBjKC05MCw5MCkgJiByZWdpb24gPT0gIkVXIildLCAKICAgICAgICAgICAgICBhZXMoeCA9IFgsIHkgPSBZLCBncm91cCA9IGdyb3VwKSwgCiAgICAgICAgICAgICAgbGluZXR5cGUgPSAic29saWQiLCBjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gLjMpICsKICAgICMgYWRkIGdyYXRpY3VsZSBsYWJlbHMKICAgIGdlb21fdGV4dChkYXRhID0gZ3JpZC5sYmwuRFQsICMgbGF0aXR1ZGUKICAgICAgICAgICAgICBhZXMoeCA9IFgsIHkgPSBZLCBsYWJlbCA9IGxibCksIAogICAgICAgICAgICAgIGNvbG91ciA9ICJncmV5NTAiLCBzaXplID0gMikgKwogICAgIyBlbnN1cmVzIHRoYXQgb25lIHVuaXQgb24gdGhlIHgtYXhpcyBpcyB0aGUgc2FtZSBsZW5ndGggYXMgb25lIHVuaXQgb24gdGhlIHktYXhpcwogICAgY29vcmRfZXF1YWwoKSArICMgc2FtZSBhcyBjb29yZF9maXhlZChyYXRpbyA9IDEpCiAgICAjIHNldCBlbXB0eSB0aGVtZQogICAgdGhlbWVfdm9pZCgpCgpgYGAKCmBgYHtyIG1hcHBpbmcgZWFjaCByZWdpb259CgpyZWdpb25fbWFwcyA8LSBsaXN0KCkKcmVnaW9uc19zaGlmdF9wcm9qZWN0aW9uIDwtIGMoIndlc3RfcGFjX3NwZGZfc2hpZnQiLCAiZWFzdF9wYWNfc3BkZl9zaGlmdCIpCgoKI2NoYW5nZSBuYW1lcyB0byBtYXRjaCBuZXcgbWVyZ2VkIHJlZ2lvbnMgd2l0aCBub24tTE1FcyAobWFza19mdWxsKQplYXN0X3BhY19zcGRmX3NoaWZ0X21hc2tfZnVsbCA8LSBlYXN0X3BhY19zcGRmX3NoaWZ0X21hc2tfMXMKd2VzdF9hdGxfc3BkZl9tYXNrX2Z1bGwgPC0gd2VzdF9hdGxfc3BkZl9tYXNrXzFzCndlc3RfaW5kX3NwZGZfbWFza19mdWxsIDwtIHdlc3RfaW5kX3NwZGZfbWFza18xcwplYXN0X2F0bF9zcGRmX25vYnVmX21hc2tfZnVsbCA8LSBlYXN0X2F0bF9zcGRmX25vYnVmX21hc2tfMXMKd2VzdF9wYWNfc3BkZl9zaGlmdF9tYXNrX2Z1bGwgPC0gd2VzdF9wYWNfc3BkZl9tYXNrX2Z1bGwKZWFzdF9pbmRfc3BkZl9tYXNrX2Z1bGwgPC0gZWFzdF9pbmRfc3BkZl9tYXNrXzFzCgoKCgpmb3IgKGkgaW4gMTpsZW5ndGgocmVnaW9uX25hbWVzKSkgewogIAogIHJlZ2lvbl9zcGRmIDwtIGdldChwYXN0ZTAocmVnaW9uX25hbWVzW2ldLCAiX21hc2siKSkKICAKICBpZihyZWdpb25fbmFtZXNbaV0gJWluJSByZWdpb25zX3NoaWZ0X3Byb2plY3Rpb24pIHsKICAKICAjcGFjaWZpYyBjZW50ZXJlZCBwcm9qZWN0aW9uCiAgCiAgcmVnaW9uX3NwZGZfbWFza18xc19leHRlbnQgPC0gZXh0ZW50KGdldChwYXN0ZTAocmVnaW9uX25hbWVzW2ldLCJfbWFza19mdWxsIikpKSAjIHRha2UgZXh0ZW50IG9mIHJlZ2lvbgogIAogICNjb252ZXJ0IHJhc3RlcnMgdG8gZGZzIGRhdGEgZnJhbWUKICByZWdpb25fc3BkZiA8LSBhcyhnZXQocGFzdGUwKHJlZ2lvbl9uYW1lc1tpXSwiX21hc2tfZnVsbCIpKSwgIlNwYXRpYWxQaXhlbHNEYXRhRnJhbWUiKQogIHJlZ2lvbl9kZiA8LSBhcy5kYXRhLmZyYW1lKHJlZ2lvbl9zcGRmKQogIGNvbG5hbWVzKHJlZ2lvbl9kZikgPC0gYygidmFsdWUiLCAieCIsICJ5IikKICAKICAKICAocmVnaW9uX21hcHNbW2ldXSA8LSBnZ3Bsb3QoKSArIAogICAgIyBhZGQgcHJvamVjdGVkIGNvdW50cmllcwogICAgZ2VvbV9wb2x5Z29uKGRhdGEgPSBDb3VudHJ5LkRUX3NoaWZ0LCAKICAgICAgICAgICAgICAgICBhZXMoeCA9IGxvbmcubmV3KzE1MCwgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksIAogICAgICAgICAgICAgICAgIGNvbG91ciA9ICJncmF5NzAiLCAKICAgICAgICAgICAgICAgICBmaWxsID0gImdyYXk5MCIsIAogICAgICAgICAgICAgICAgIHNpemUgPSAwLjI1KSArCiAgICBnZW9tX3RpbGUoZGF0YSA9IHJlZ2lvbl9kZiwgYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IHZhbHVlLCBjb2xvciA9ICJsaWdodHN0ZWVsYmx1ZTQiKSwgY29sb3IgPSAibGlnaHRzdGVlbGJsdWU0IikgKwogICAgY29vcmRfc2YoeCA9IGMocmVnaW9uX3NwZGZfbWFza18xc19leHRlbnRbMV0sIHJlZ2lvbl9zcGRmX21hc2tfMXNfZXh0ZW50WzJdKSwgeSA9IGMocmVnaW9uX3NwZGZfbWFza18xc19leHRlbnRbM10sIHJlZ2lvbl9zcGRmX21hc2tfMXNfZXh0ZW50WzRdKSkgKwogICAgbGFicyggeCA9IHBhc3RlMCgiTG9uZ2l0dWRlICIsICJcdTAwQjBFIiksIHkgPSBwYXN0ZTAoIkxhdGl0dWRlICIsICJcdTAwQjBFIikpICsKICAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMCkgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikpCiAgCiAgZmlsZW5hbWUgPC0gcGFzdGUwKHJlZ2lvbl9uYW1lc1tpXSwgIl9tYXBfMmRlZ3JlZXMuanBnIikKICBnZ3NhdmUocGxvdCA9IHJlZ2lvbl9tYXBzW1tpXV0sIGZpbGVuYW1lID0gZmlsZW5hbWUsIGhlaWdodCA9IDQsIHVuaXRzID0gYygiaW4iKSkKICAKICB9IGVsc2UgewoKICAjYXRsYW50aWMgY2VudGVyZWQgcHJvamVjdGlvbgogIHJlZ2lvbl9zcGRmX21hc2tfMXNfZXh0ZW50IDwtIGV4dGVudChnZXQocGFzdGUwKHJlZ2lvbl9uYW1lc1tpXSwiX21hc2tfZnVsbCIpKSkgIyB0YWtlIGV4dGVudCBvZiByZWdpb24KICAKICAjY29udmVydCByYXN0ZXJzIHRvIGRmcyBkYXRhIGZyYW1lCiAgcmVnaW9uX3NwZGYgPC0gYXMoZ2V0KHBhc3RlMChyZWdpb25fbmFtZXNbaV0sIl9tYXNrX2Z1bGwiKSksICJTcGF0aWFsUGl4ZWxzRGF0YUZyYW1lIikKICByZWdpb25fZGYgPC0gYXMuZGF0YS5mcmFtZShyZWdpb25fc3BkZikKICBjb2xuYW1lcyhyZWdpb25fZGYpIDwtIGMoInZhbHVlIiwgIngiLCAieSIpCiAgCiAgCiAgKHJlZ2lvbl9tYXBzW1tpXV0gPC0gZ2dwbG90KCkgKyAKICAgICMgYWRkIHByb2plY3RlZCBjb3VudHJpZXMKICAgIGdlb21fcG9seWdvbihkYXRhID0gQ291bnRyeS5EVCwgCiAgICAgICAgICAgICAgICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgCiAgICAgICAgICAgICAgICAgY29sb3VyID0gImdyYXk3MCIsIAogICAgICAgICAgICAgICAgIGZpbGwgPSAiZ3JheTkwIiwgCiAgICAgICAgICAgICAgICAgc2l6ZSA9IDAuMjUpICsKICAgIGdlb21fdGlsZShkYXRhID0gcmVnaW9uX2RmLCBhZXMoeCA9IHgsIHkgPSB5LCBmaWxsID0gYXMuZmFjdG9yKHZhbHVlKSkpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gImxpZ2h0c3RlZWxibHVlNCIsIGxhYmVscyA9ICJDb250aW5lbnRhbCBTaGVsZiBBcmVhIikgKwogICAgY29vcmRfc2YoeCA9IGMocmVnaW9uX3NwZGZfbWFza18xc19leHRlbnRbMV0sIHJlZ2lvbl9zcGRmX21hc2tfMXNfZXh0ZW50WzJdKSwgeSA9IGMocmVnaW9uX3NwZGZfbWFza18xc19leHRlbnRbM10sIHJlZ2lvbl9zcGRmX21hc2tfMXNfZXh0ZW50WzRdKSkgKwogICAgbGFicyggeCA9IHBhc3RlMCgiTG9uZ2l0dWRlICIsICJcdTAwQjBFIiksIHkgPSBwYXN0ZTAoIkxhdGl0dWRlICIsICJcdTAwQjBOIikpICsKICAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMCkgKwogICAgdGhlbWVfY2xhc3NpYygpICsKICAgICAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSkKICAKICBmaWxlbmFtZSA8LSBwYXN0ZTAocmVnaW9uX25hbWVzW2ldLCAiX21hcF8yZGVncmVlcy5qcGciKQogIGdnc2F2ZShwbG90ID0gcmVnaW9uX21hcHNbW2ldXSwgZmlsZW5hbWUgPSBmaWxlbmFtZSwgaGVpZ2h0ID0gNCwgdW5pdHMgPSBjKCJpbiIpKQogIAogIH0KICAKfQoKcmVnaW9uX21hcF9sZWdlbmQgPC0gZ2V0X2xlZ2VuZChyZWdpb25fbWFwc1tbNl1dKQoKI3NhdmUgbWFwcwpzYXZlKHJlZ2lvbl9tYXBzLCBmaWxlID0gaGVyZTo6aGVyZSgiRmlndXJlcyIsIkZpZ3VyZTNfNSIsICJyZWdpb25fbWFwcy5SRGF0YSIpKQojc2F2ZSBtYXAgbGVnZW5kCnNhdmUocmVnaW9uX21hcF9sZWdlbmQsIGZpbGUgPSBoZXJlOjpoZXJlKCJGaWd1cmVzIiwiRmlndXJlM181IiwgInJlZ2lvbl9tYXBzX2xlZ2VuZC5SRGF0YSIpKQoKI3NhdmUgYWxsIGFyZWEgdmVyc3VzIGxhdGl0dWRlIHBsb3RzIApzYXZlKGFyZWFfbGF0aXR1ZGVfZWFzdF9wYWMgLCBhcmVhX2xhdGl0dWRlX3dlc3RfcGFjICwgYXJlYV9sYXRpdHVkZV9lYXN0X2F0bCAsIGFyZWFfbGF0aXR1ZGVfd2VzdF9hdGwgLCBhcmVhX2xhdGl0dWRlX2Vhc3RfaW5kICwgYXJlYV9sYXRpdHVkZV93ZXN0X2luZCwgZmlsZSA9IGhlcmU6OmhlcmUoIkZpZ3VyZXMiLCJGaWd1cmUzXzUiLCAibGF0aXR1ZGVfMmRlZ3JlZV9tYXBzLlJEYXRhIikpCmBgYAoKQ29tYmluaW5nIHBsb3RzCgpyZWdpb25fbWFwczogCiJ3ZXN0X3BhY19zcGRmX3NoaWZ0IiwgCiJlYXN0X3BhY19zcGRmX3NoaWZ0IiwgCiJ3ZXN0X2F0bF9zcGRmIiwgCiJ3ZXN0X2luZF9zcGRmIiwgCiJlYXN0X2F0bF9zcGRmX25vYnVmIiwgCiJlYXN0X2luZF9zcGRmIgoKUGxvdHMgb2YgYXJlYSB2ZXJzdXMgbGF0aXR1ZGUKYXJlYV9sYXRpdHVkZV9lYXN0X3BhYwphcmVhX2xhdGl0dWRlX3dlc3RfcGFjCmFyZWFfbGF0aXR1ZGVfZWFzdF9hdGwKYXJlYV9sYXRpdHVkZV93ZXN0X2F0bAphcmVhX2xhdGl0dWRlX2Vhc3RfaW5kCmFyZWFfbGF0aXR1ZGVfd2VzdF9pbmQKCk5vdywgY29tYmluZSBwbG90cwoKYGBge3IgY29tYmluaW5nIHBsb3RzfQpsaWJyYXJ5KGVnZykKbGlicmFyeShnZ3B1YnIpCgojd2VzdCBwYWNpZmljCih3ZXN0X3BhY2lmaWNfbWVyZ2VfbWFwX3Bsb3QgPC0gZWdnOjpnZ2FycmFuZ2UocmVnaW9uX21hcHNbWzFdXSwgCiAgICAgICAgICBhcmVhX2xhdGl0dWRlX3dlc3RfcGFjIAogICAgICAgICAgKyAKICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpICksIAoKICAgICAgICAgIG5yb3cgPSAxLAogICAgICAgICAgdG9wID0gVCwKICAgICAgICAgIHdpZHRocyA9IGMoMiwxKSkpCgpnZ3NhdmUocGxvdCA9IHdlc3RfcGFjaWZpY19tZXJnZV9tYXBfcGxvdCwgZmlsZW5hbWUgPSAid2VzdF9wYWNpZmljX21lcmdlX21hcF9wbG90XzJkZWdyZWVzLmpwZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMywgdW5pdHMgPSAiaW4iKQoKI2Vhc3QgcGFjaWZpYwooZWFzdF9wYWNpZmljX21lcmdlX21hcF9wbG90IDwtIGVnZzo6Z2dhcnJhbmdlKHJlZ2lvbl9tYXBzW1syXV0sIAogICAgICAgICAgYXJlYV9sYXRpdHVkZV9lYXN0X3BhYyAKICAgICAgICAgICsgCiAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSApCiAgICAgICAgICAsIAoKICAgICAgICAgIG5yb3cgPSAxLAogICAgICAgICAgdG9wID0gVAogICAgICAgICAgLCAKICAgICAgICAgIHdpZHRocyA9IGMoMiwxKQogICkpCgpnZ3NhdmUocGxvdCA9IGVhc3RfcGFjaWZpY19tZXJnZV9tYXBfcGxvdCwgZmlsZW5hbWUgPSAiZWFzdF9wYWNpZmljX21lcmdlX21hcF9wbG90XzJkZWdyZWVzLmpwZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMywgdW5pdHMgPSAiaW4iKQoKI2JvdGggdG9nZXRoZXIKbGlicmFyeShjb3dwbG90KQpmaWd1cmVfM19wYWNpZmljX21lcmdlX3RvcCA8LSBlZ2c6OmdnYXJyYW5nZShyZWdpb25fbWFwc1tbMV1dICsgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLCBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCJjbSIpKSwKICAgICAgICAgIGFyZWFfbGF0aXR1ZGVfd2VzdF9wYWMgKyAKICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwiY20iKSksCiAgICAgICAgICBucm93ID0gMSwgbmNvbCA9IDIsIHRvcCA9IFQsIGJvdHRvbSA9IFQsIHdpZHRocyA9IGMoMS41LDEpKQoKZmlndXJlXzNfcGFjaWZpY19tZXJnZV9ib3R0b20gPC0gZWdnOjpnZ2FycmFuZ2UocmVnaW9uX21hcHNbWzJdXSArIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksImNtIikpLAogICAgICAgICAgYXJlYV9sYXRpdHVkZV9lYXN0X3BhYyArIAogICAgICAgICAgICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwiY20iKSksCiAgICAgICAgICBucm93ID0gMSwgbmNvbCA9IDIsIHRvcCA9IFQsIGJvdHRvbSA9IFQsIHdpZHRocyA9IGMoMS41LDEpKQoKbGVnZW5kIDwtIGdldF9sZWdlbmQoYXJlYV9sYXRpdHVkZV9lYXN0X3BhYykKCmZpZ3VyZV8zX3BhY2lmaWNfbWVyZ2UgPC0gcGxvdF9ncmlkKGZpZ3VyZV8zX3BhY2lmaWNfbWVyZ2VfdG9wLCBmaWd1cmVfM19wYWNpZmljX21lcmdlX2JvdHRvbSwgbmNvbCA9IDEsIG5yb3cgPSAyLCBsYWJlbHMgPSBjKCJhLiIsImIuIiksIGF4aXMgPSAibCIsIGFsaWduID0gInYiLCBoanVzdCA9IC03LCByZWxfaGVpZ2h0cyA9IGMoMSwxLjUpKQoKZmlndXJlXzNfcGFjaWZpY19tZXJnZV9sZWdlbmQgPC0gcGxvdF9ncmlkKGZpZ3VyZV8zX3BhY2lmaWNfbWVyZ2UsIGxlZ2VuZCwgbmNvbCA9IDIsIG5yb3cgPSAxLCByZWxfd2lkdGhzID0gYyg0LDEpKQoKZ2dzYXZlKGZpZ3VyZV8zX3BhY2lmaWNfbWVyZ2VfbGVnZW5kLCBwYXRoID0gaGVyZTo6aGVyZSgiRmlndXJlcyIsICJGaWd1cmUzXzUiKSwgZmlsZW5hbWUgPSAiRmlndXJlM19QYWNpZmljX2xhdGl0dWRlX2FyZWEuanBnIiwgaGVpZ2h0ID0gNCwgd2lkdGggPSA2LCB1bml0ID0gImluIikKCmBgYAoKCmBgYHtyIGF0bGFudGljIG9jZWFufQojZWFzdCBhdGxhbnRpYwooZWFzdF9hdGxhbnRpY19tZXJnZV9tYXBfcGxvdCA8LSBlZ2c6OmdnYXJyYW5nZShyZWdpb25fbWFwc1tbNV1dLCAKICAgICAgICAgIGFyZWFfbGF0aXR1ZGVfZWFzdF9hdGwKICAgICAgICAgICsgCiAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSApCiAgICAgICAgICAsIAoKICAgICAgICAgIG5yb3cgPSAxLAogICAgICAgICAgdG9wID0gVCwKICAgICAgICAgIHdpZHRocyA9IGMoMiwxKSkpCgpnZ3NhdmUocGxvdCA9IGVhc3RfYXRsYW50aWNfbWVyZ2VfbWFwX3Bsb3QsIGZpbGVuYW1lID0gImVhc3RfYXRsYW50aWNfbWVyZ2VfbWFwX3Bsb3RfMmRlZ3JlZXMuanBnIiwgd2lkdGggPSA3LCBoZWlnaHQgPSAzLCB1bml0cyA9ICJpbiIpCgojd2VzdCBhdGxhbnRpYwood2VzdF9hdGxhbnRpY19tZXJnZV9tYXBfcGxvdCA8LSBlZ2c6OmdnYXJyYW5nZShyZWdpb25fbWFwc1tbM11dLCAKICAgICAgICAgIGFyZWFfbGF0aXR1ZGVfd2VzdF9hdGwKICAgICAgICAgICsgCiAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSApCiAgICAgICAgICAsIAoKICAgICAgICAgIG5yb3cgPSAxLAogICAgICAgICAgdG9wID0gVCwgCiAgICAgICAgICB3aWR0aHMgPSBjKDIsMSkpKQoKZ2dzYXZlKHBsb3QgPSB3ZXN0X2F0bGFudGljX21lcmdlX21hcF9wbG90LCBmaWxlbmFtZSA9ICJ3ZXN0X2F0bGFudGljX21lcmdlX21hcF9wbG90XzJkZWdyZWVzLmpwZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMywgdW5pdHMgPSAiaW4iKQoKI2JvdGggdG9nZXRoZXIKbGlicmFyeShjb3dwbG90KQpmaWd1cmVfNF9hdGxhbnRpY19tZXJnZV90b3AgPC0gZWdnOjpnZ2FycmFuZ2UocmVnaW9uX21hcHNbWzNdXSArIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgcGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwiY20iKSksCiAgICAgICAgICBhcmVhX2xhdGl0dWRlX3dlc3RfYXRsICsgCiAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksImNtIikpLAogICAgICAgICAgbnJvdyA9IDEsIG5jb2wgPSAyLCB0b3AgPSBULCBib3R0b20gPSBULCB3aWR0aHMgPSBjKDEuNSwxKSkKCmZpZ3VyZV80X2F0bGFudGljX21lcmdlX2JvdHRvbSA8LSBlZ2c6OmdnYXJyYW5nZShyZWdpb25fbWFwc1tbNV1dICsgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwiY20iKSksCiAgICAgICAgICBhcmVhX2xhdGl0dWRlX2Vhc3RfYXRsICsgCiAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICAgICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCJjbSIpKSwKICAgICAgICAgIG5yb3cgPSAxLCBuY29sID0gMiwgdG9wID0gVCwgYm90dG9tID0gVCwgd2lkdGhzID0gYygxLjUsMSkpCgpsZWdlbmQgPC0gZ2V0X2xlZ2VuZChhcmVhX2xhdGl0dWRlX2Vhc3RfYXRsKQoKZmlndXJlXzRfYXRsYW50aWNfbWVyZ2UgPC0gcGxvdF9ncmlkKGZpZ3VyZV80X2F0bGFudGljX21lcmdlX3RvcCwgZmlndXJlXzRfYXRsYW50aWNfbWVyZ2VfYm90dG9tLCBuY29sID0gMSwgbnJvdyA9IDIsIGxhYmVscyA9IGMoImEuIiwiYi4iKSwgYXhpcyA9ICJsIiwgYWxpZ24gPSAidiIsIGhqdXN0ID0gLTcsIHJlbF9oZWlnaHRzID0gYygxLjEsMSkpCgpmaWd1cmVfNF9hdGxhbnRpY19tZXJnZV9sZWdlbmQgPC0gcGxvdF9ncmlkKGZpZ3VyZV80X2F0bGFudGljX21lcmdlLCBsZWdlbmQsIG5jb2wgPSAyLCBucm93ID0gMSwgcmVsX3dpZHRocyA9IGMoNCwxKSkKCmdnc2F2ZShmaWd1cmVfNF9hdGxhbnRpY19tZXJnZV9sZWdlbmQsIHBhdGggPSBoZXJlOjpoZXJlKCJGaWd1cmVzIiwgIkZpZ3VyZTNfNSIpLCBmaWxlbmFtZSA9ICJGaWd1cmU0X2F0bGFudGljX2xhdGl0dWRlX2FyZWEuanBnIiwgaGVpZ2h0ID0gNCwgd2lkdGggPSA2LCB1bml0ID0gImluIikKYGBgCgpgYGB7ciBpbmRpYW4gb2NlYW59CiN3ZXN0IGluZGlhbgood2VzdF9pbmRpYW5fbWVyZ2VfbWFwX3Bsb3QgPC0gZWdnOjpnZ2FycmFuZ2UocmVnaW9uX21hcHNbWzRdXSwgCiAgICAgICAgICBhcmVhX2xhdGl0dWRlX3dlc3RfaW5kIAogICAgICAgICAgKyAKICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpICkKICAgICAgICAgICwgCgogICAgICAgICAgbnJvdyA9IDEsCiAgICAgICAgICB0b3AgPSBUKSkKCmdnc2F2ZShwbG90ID0gd2VzdF9pbmRpYW5fbWVyZ2VfbWFwX3Bsb3QsIGZpbGVuYW1lID0gIndlc3RfaW5kaWFuX21lcmdlX21hcF9wbG90XzJkZWdyZWVzLmpwZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMywgdW5pdHMgPSAiaW4iKQoKCiNlYXN0IGluZGlhbgooZWFzdF9pbmRpYW5fbWVyZ2VfbWFwX3Bsb3QgPC0gZWdnOjpnZ2FycmFuZ2UocmVnaW9uX21hcHNbWzZdXSwgCiAgICAgICAgICBhcmVhX2xhdGl0dWRlX2Vhc3RfaW5kCiAgICAgICAgICArIAogICAgICAgICAgICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkgKQogICAgICAgICAgLCAKCiAgICAgICAgICBucm93ID0gMSwKICAgICAgICAgIHRvcCA9IFQpKQoKZ2dzYXZlKHBsb3QgPSBlYXN0X2luZGlhbl9tZXJnZV9tYXBfcGxvdCwgZmlsZW5hbWUgPSAiZWFzdF9pbmRpYW5fbWVyZ2VfbWFwX3Bsb3RfMmRlZ3JlZXMuanBnIiwgd2lkdGggPSA3LCBoZWlnaHQgPSAzLCB1bml0cyA9ICJpbiIpCgojYm90aCB0b2dldGhlcgpsaWJyYXJ5KGNvd3Bsb3QpCmZpZ3VyZV81X2luZGlhbl9tZXJnZV90b3AgPC0gZWdnOjpnZ2FycmFuZ2UocmVnaW9uX21hcHNbWzRdXSArIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgcGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwiY20iKSksCiAgICAgICAgICBhcmVhX2xhdGl0dWRlX3dlc3RfaW5kICsgCiAgICAgICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksImNtIikpLAogICAgICAgICAgbnJvdyA9IDEsIG5jb2wgPSAyLCB0b3AgPSBULCBib3R0b20gPSBULCB3aWR0aHMgPSBjKDEuNSwxKSkKCmZpZ3VyZV81X2luZGlhbl9tZXJnZV9ib3R0b20gPC0gZWdnOjpnZ2FycmFuZ2UocmVnaW9uX21hcHNbWzZdXSArIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksImNtIikpLAogICAgICAgICAgYXJlYV9sYXRpdHVkZV9lYXN0X2luZCArIAogICAgICAgICAgICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwiY20iKSksCiAgICAgICAgICBucm93ID0gMSwgbmNvbCA9IDIsIHRvcCA9IFQsIGJvdHRvbSA9IFQsIHdpZHRocyA9IGMoMS41LDEpKQoKbGVnZW5kIDwtIGdldF9sZWdlbmQoYXJlYV9sYXRpdHVkZV9lYXN0X2luZCkKCmZpZ3VyZV81X2luZGlhbl9tZXJnZSA8LSBwbG90X2dyaWQoZmlndXJlXzVfaW5kaWFuX21lcmdlX3RvcCwgZmlndXJlXzVfaW5kaWFuX21lcmdlX2JvdHRvbSwgbmNvbCA9IDEsIG5yb3cgPSAyLCBsYWJlbHMgPSBjKCJhLiIsImIuIiksIGF4aXMgPSAibCIsIGFsaWduID0gInYiLCBoanVzdCA9IC03LCByZWxfaGVpZ2h0cyA9IGMoMSwxLjIpKQoKZmlndXJlXzVfaW5kaWFuX21lcmdlX2xlZ2VuZCA8LSBwbG90X2dyaWQoZmlndXJlXzVfaW5kaWFuX21lcmdlLCBsZWdlbmQsIG5jb2wgPSAyLCBucm93ID0gMSwgcmVsX3dpZHRocyA9IGMoNCwxKSkKCmdnc2F2ZShmaWd1cmVfNV9pbmRpYW5fbWVyZ2VfbGVnZW5kLCBwYXRoID0gaGVyZTo6aGVyZSgiRmlndXJlcyIsICJGaWd1cmUzXzUiKSwgZmlsZW5hbWUgPSAiRmlndXJlNV9pbmRpYW5fbGF0aXR1ZGVfYXJlYS5qcGciLCBoZWlnaHQgPSA0LCB3aWR0aCA9IDYsIHVuaXQgPSAiaW4iKQpgYGAKQW5kIG5vdyBmaW5hbCBtZXJnZSBmb3IgZmlndXJlcyAzLTUKYGBge3IgZmluYWwgbWVyZ2UgZmlndXJlc30KaW5kaWFuX2xhdGl0dWRlXzJfcGxvdHMgPC0gZ2dhcnJhbmdlKHdlc3RfaW5kaWFuX21lcmdlX21hcF9wbG90LCBlYXN0X2luZGlhbl9tZXJnZV9tYXBfcGxvdCwgbnJvdyA9IDIsIG5jb2wgPSAxKQpnZ3NhdmUoaW5kaWFuX2xhdGl0dWRlXzJfcGxvdHMsIGZpbGUgPSAiaW5kaWFuX2xhdGl0dWRlXzJfcGxvdHMuanBnIiwgaGVpZ2h0ID0gNiwgdW5pdCA9ICJpbiIpCmdnc2F2ZShpbmRpYW5fbGF0aXR1ZGVfMl9wbG90cywgZmlsZSA9ICJpbmRpYW5fbGF0aXR1ZGVfMl9wbG90cy5wZGYiLCBoZWlnaHQgPSA2LCB1bml0ID0gImluIikKCnBhY2lmaWNfbGF0aXR1ZGVfMl9wbG90cyA8LSBnZ2FycmFuZ2Uod2VzdF9wYWNpZmljX21lcmdlX21hcF9wbG90LCBlYXN0X3BhY2lmaWNfbWVyZ2VfbWFwX3Bsb3QsIG5yb3cgPSAyLCBuY29sID0gMSkKZ2dzYXZlKHBhY2lmaWNfbGF0aXR1ZGVfMl9wbG90cywgZmlsZSA9ICJwYWNpZmljX2xhdGl0dWRlXzJfcGxvdHMuanBnIiwgaGVpZ2h0ID0gNiwgdW5pdCA9ICJpbiIpCmdnc2F2ZShwYWNpZmljX2xhdGl0dWRlXzJfcGxvdHMsIGZpbGUgPSAicGFjaWZpY19sYXRpdHVkZV8yX3Bsb3RzLnBkZiIsIGhlaWdodCA9IDYsIHVuaXQgPSAiaW4iKQoKYXRsYW50aWNfbGF0aXR1ZGVfMl9wbG90cyA8LSBnZ2FycmFuZ2Uod2VzdF9hdGxhbnRpY19tZXJnZV9tYXBfcGxvdCwgZWFzdF9hdGxhbnRpY19tZXJnZV9tYXBfcGxvdCwgbnJvdyA9IDIsIG5jb2wgPSAxKQpnZ3NhdmUoYXRsYW50aWNfbGF0aXR1ZGVfMl9wbG90cywgZmlsZSA9ICJhdGxhbnRpY19sYXRpdHVkZV8yX3Bsb3RzLmpwZyIsIGhlaWdodCA9IDYsIHVuaXQgPSAiaW4iKQpnZ3NhdmUoYXRsYW50aWNfbGF0aXR1ZGVfMl9wbG90cywgZmlsZSA9ICJhdGxhbnRpY19sYXRpdHVkZV8yX3Bsb3RzLnBkZiIsIGhlaWdodCA9IDYsIHVuaXQgPSAiaW4iKQpgYGAKCgpTdW1tYXJ5IFRhYmxlIG9mIFNoaWZ0cyBhd2F5IGZyb20gRXF1YXRvcgoKCmBgYHtyIHN0YXRpc3RpY3MgdGFibGV9CnJlZ2lvbmFsX3N0YXRpc3RpY3MgPC0gZGF0YS50YWJsZSgpCnJlZ2lvbmFsX3N0YXRpc3RpY3NbLHJlZ2lvbiA6PSByZXAoYygiV2VzdCBQYWNpZmljIiwgIkVhc3QgUGFjaWZpYyIsICJXZXN0IEF0bGFudGljIiwgIldlc3QgSW5kaWFuIiwgIkVhc3QgQXRsYW50aWMiLCAiRWFzdCBJbmRpYW4iKSxlYWNoID0gMildWyxoZW1pc3BoZXJlIDo9IHJlcChjKCJOb3J0aGVybiIsICJTb3V0aGVybiIpLDYpXVssY29udHJhY3Rpb25zIDo9IGFzLm51bWVyaWMoTkEpXVssZXhwYW5zaW9ucyA6PSBhcy5udW1lcmljKE5BKV1bLGNvZWYgOj0gYXMubnVtZXJpYyhOQSldWyxwdmFsdWUgOj0gYXMubnVtZXJpYyhOQSldCgpzaGVsZl9hcmVhcyA8LSBjKCJ3ZXN0X3BhY19zaGVsZl9hcmVhcyIsICJlYXN0X3BhY19zaGVsZl9hcmVhcyIsICJ3ZXN0X2F0bF9zaGVsZl9hcmVhcyIsICJ3ZXN0X2luZF9zaGVsZl9hcmVhcyIsICJlYXN0X2F0bF9zaGVsZl9hcmVhcyIsICJlYXN0X2luZF9zaGVsZl9hcmVhcyIpCgpub3J0aF9tb2RzIDwtIGMoIndlc3RfcGFjX25vcnRoX21vZCIsICJlYXN0X3BhY19ub3J0aF9tb2QiLCAid2VzdF9hdGxfbm9ydGhfbW9kIiwgIndlc3RfaW5kX25vcnRoX21vZCIsICJlYXN0X2F0bF9ub3J0aF9tb2QiLCAiZWFzdF9pbmRfbm9ydGhfbW9kIikKCnNvdXRoX21vZHMgPC0gYygid2VzdF9wYWNfc291dGhfbW9kIiwgImVhc3RfcGFjX3NvdXRoX21vZCIsICJ3ZXN0X2F0bF9zb3V0aF9tb2QiLCAid2VzdF9pbmRfc291dGhfbW9kIiwgImVhc3RfYXRsX3NvdXRoX21vZCIsICJlYXN0X2luZF9zb3V0aF9tb2QiKQoKZm9yIChpIGluIDE6bGVuZ3RoKHNoZWxmX2FyZWFzKSkgewoKICB0aGlzX3NoZWxmX2FyZWEgPC0gZ2V0KHNoZWxmX2FyZWFzW2ldKQogICAgI3BvcHVsYXRlIHdlc3QgcGFjaWZpYwogICAgcmVnaW9uYWxfc3RhdGlzdGljc1tyZWdpb24gPT0gdW5pcXVlKHJlZ2lvbmFsX3N0YXRpc3RpY3NbLHJlZ2lvbl0pW2ldICYgaGVtaXNwaGVyZSA9PSAiTm9ydGhlcm4iLCAiY29udHJhY3Rpb25zIl0gPC0gcm91bmQodGhpc19zaGVsZl9hcmVhW2xhdGl0dWRlX2VuZCA+IDAgJiBjaGFuZ2VfYWJvdmVfMmZvbGQgPT0gLTEsLk5dL25yb3codGhpc19zaGVsZl9hcmVhW2xhdGl0dWRlX2VuZCA+IDAgJiAhaXMubmEocGVyY2VudF9jaGFuZ2UpXSksNCkqMTAwICNub3J0aGVybiBoZW1pc3BoZXJlIGNvbnRyYWN0aW9uCiAgICAKICAgIHJlZ2lvbmFsX3N0YXRpc3RpY3NbcmVnaW9uID09IHVuaXF1ZShyZWdpb25hbF9zdGF0aXN0aWNzWyxyZWdpb25dKVtpXSAmIGhlbWlzcGhlcmUgPT0gIlNvdXRoZXJuIiwgImNvbnRyYWN0aW9ucyJdIDwtIHJvdW5kKHRoaXNfc2hlbGZfYXJlYVtsYXRpdHVkZV9lbmQgPCAwICYgY2hhbmdlX2Fib3ZlXzJmb2xkID09IC0xLC5OXS9ucm93KHRoaXNfc2hlbGZfYXJlYVtsYXRpdHVkZV9lbmQgPCAwICYgIWlzLm5hKHBlcmNlbnRfY2hhbmdlKV0pLDQpKjEwMCAjc291dGhlcm4gaGVtaXNwaGVyZSBjb250cmFjdGlvbgogICAgCiAgICByZWdpb25hbF9zdGF0aXN0aWNzW3JlZ2lvbiA9PSB1bmlxdWUocmVnaW9uYWxfc3RhdGlzdGljc1sscmVnaW9uXSlbaV0gJiBoZW1pc3BoZXJlID09ICJOb3J0aGVybiIsICJleHBhbnNpb25zIl0gPC0gcm91bmQodGhpc19zaGVsZl9hcmVhW2xhdGl0dWRlX2VuZCA+IDAgJiBjaGFuZ2VfYWJvdmVfMmZvbGQgPT0gMSwuTl0vbnJvdyh0aGlzX3NoZWxmX2FyZWFbbGF0aXR1ZGVfZW5kID4gMCAmICFpcy5uYShwZXJjZW50X2NoYW5nZSldKSw0KSoxMDAgI25vcnRoZXJuIGhlbWlzcGhlcmUgZXhwYW5zaW9uCiAgICAKICAgIHJlZ2lvbmFsX3N0YXRpc3RpY3NbcmVnaW9uID09IHVuaXF1ZShyZWdpb25hbF9zdGF0aXN0aWNzWyxyZWdpb25dKVtpXSAmIGhlbWlzcGhlcmUgPT0gIlNvdXRoZXJuIiwgImV4cGFuc2lvbnMiXSA8LSByb3VuZCh0aGlzX3NoZWxmX2FyZWFbbGF0aXR1ZGVfZW5kIDwgMCAmIGNoYW5nZV9hYm92ZV8yZm9sZCA9PSAxLC5OXS9ucm93KHRoaXNfc2hlbGZfYXJlYVtsYXRpdHVkZV9lbmQgPCAwICYgIWlzLm5hKHBlcmNlbnRfY2hhbmdlKV0pLDQpKjEwMCAjc291dGhlcm4gaGVtaXNwaGVyZSBleHBhbnNpb24KICAgIAogICAgI25vcnRoZXJuIG1vZAogICAgcmVnaW9uYWxfc3RhdGlzdGljc1tyZWdpb24gPT0gdW5pcXVlKHJlZ2lvbmFsX3N0YXRpc3RpY3NbLHJlZ2lvbl0pW2ldICYgaGVtaXNwaGVyZSA9PSAiTm9ydGhlcm4iLCAiY29lZiJdIDwtIHJvdW5kKGdldChub3J0aF9tb2RzW2ldKSRjb2VmZmljaWVudHNbMl0sMikKICAgIHJlZ2lvbmFsX3N0YXRpc3RpY3NbcmVnaW9uID09IHVuaXF1ZShyZWdpb25hbF9zdGF0aXN0aWNzWyxyZWdpb25dKVtpXSAmIGhlbWlzcGhlcmUgPT0gIk5vcnRoZXJuIiwgInB2YWx1ZSJdIDwtIHJvdW5kKHN1bW1hcnkoZ2V0KG5vcnRoX21vZHNbaV0pKSRjb2VmZmljaWVudHNbLCJQcig+fHR8KSJdWzJdLDIpCiAgICAKICAgICNzb3V0aGVybiBtb2QKICAgIHJlZ2lvbmFsX3N0YXRpc3RpY3NbcmVnaW9uID09IHVuaXF1ZShyZWdpb25hbF9zdGF0aXN0aWNzWyxyZWdpb25dKVtpXSAmIGhlbWlzcGhlcmUgPT0gIlNvdXRoZXJuIiwgImNvZWYiXSA8LSByb3VuZChnZXQoc291dGhfbW9kc1tpXSkkY29lZmZpY2llbnRzWzJdLDIpCiAgICByZWdpb25hbF9zdGF0aXN0aWNzW3JlZ2lvbiA9PSB1bmlxdWUocmVnaW9uYWxfc3RhdGlzdGljc1sscmVnaW9uXSlbaV0gJiBoZW1pc3BoZXJlID09ICJTb3V0aGVybiIsICJwdmFsdWUiXSA8LSByb3VuZChzdW1tYXJ5KGdldChzb3V0aF9tb2RzW2ldKSkkY29lZmZpY2llbnRzWywiUHIoPnx0fCkiXVsyXSwyKQoKfQoKdmlldyhyZWdpb25hbF9zdGF0aXN0aWNzKQoKI3NhdmUKZndyaXRlKHJlZ2lvbmFsX3N0YXRpc3RpY3MsIGZpbGUgPSAiVGFibGUxX3JlZ2lvbmFsX3N0YXRzLmNzdiIpCmBgYAoKCmBgYHtyfQoKc2F2ZShzaWduaWZpY2FudF9jaGFuZ2VzLmxvbmcsIHNpZ25pZmljYW50X2NoYW5nZXMsIGZpbGUgPSAic2lnbmlmaWNhbnRfY2hhbmdlc18yZGVncmVlcy5SZGF0YSIpCgpgYGAKCg==